Android消息驱动模式
来源:互联网 发布:linux文件夹同步工具 编辑:程序博客网 时间:2024/06/06 01:02
我们知道Java程序开始于一个Main函数,如果只是顺序执行有限任务很快这个Main函数所在的线程就结束了。如何来保持Main函数一直存活并不断的处理已知或未知的任务呢?
1 采用死循环。但是死循环的一次循环需要处理什么任务。如果任务暂时没有,也要程序保持活跃的等待状态怎么办?
(需要:处理外来任务,可阻塞)
2 如果有两个线程或者多个线程如何来协作以完成一个微型系统任务?
(相互之间有对方的任务通知“把柄”)
我们熟悉的Windows其实是消息驱动的。由消息来通知做什么任务就做,没有消息静止等待。还有一个强大的游戏引擎cocos2d也是MainLooper内等待消息驱动。
我们的Android同样也采用了这个消息驱动模式。
Android 通过Looper MessageQueue(对应的 Native Looper和MessageQueue)Handler和Message来实现。
一:
首先,线程在启动时要在ThreadLocal内的Map中保存一个Looper对象。构造一个Looper和Thread一对一的关系 。
对于UI主线程我们在初始化Looper时传入allowQuit为false。即不可以退出主线程。
UI主线程中这样调用的:
Looper.prepareMainLooper();意味着:MessageQueue 在 Main thread not allowed to quit.
如果是在UI线程中或者已经初始化了Looper的线程我们直接用:
Handler handler = new Handler();
Handler重载了几种构造方法:
public Handler() {//直接在UI主线程、初始化了Looper的线程使用 this(null, false); } public Handler(Callback callback) {//传入消息回调处理类 this(callback, false); } public Handler(Looper looper) {//可以指定是哪个Looper的消息处理Handler this(looper, null, false); }
如果是在非UI线程并且没有Looper:
我们需要创建一个Looper,并且让其循环处理消息队列:
* Looper.prepare(); * * mHandler = new Handler() {//创建发送和和处理Message的handler * public void handleMessage(Message msg) { * // process incoming messages here * } * }; * * Looper.loop();
当然 ,以上只是表明用法,handler 的初始化可以在本线程或者其他线程的某个地方。
UI主线程ActivityThread中Main方法是UI线程的入口:(主要做的是开启一个Looper)
public static void main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}
疑问:我们知道Ui线程负责加载初始化和处理UI事件。那么如果Ui线程的main函数中就做启动了一个Looper并死循环一件事的话,UI绘画工作怎么来做呢?
这就要回到开始那句话,Android是消息驱动的。在UI进程中更是这样。在ActivityThread中我们创建一个Handler mH = new H()。用来处理AMS远程调用ApplicationThread中Activity的周期绘制函数所发出的Message。换句话说,整个Activity生命周期都是Message驱动的。
那么我们去创建Handler发送自己的消息给UI线程是为了做什么呢?
(注意,这里只配合UI线程来谈使用。消息驱动不仅仅为UI县城服务)
0 保证UI线程执行任务中尽量只做Ui相关操作
可以保证减少页面卡顿。
1 开启线程处理耗时操作
通常会因为这个原因而创建自己的线程并传入handler。
传入handler是为了 非线程处理UI事件
我觉得首先要理解ANR机制:
Activity 响应超时(InputEvents消息响应超时),五秒超时。
Broadcast onreciver。运行在主线程中的无状态类。前台10
Service 20
这三个组件都运行在主线程中,都要确保不要超出限制的组件处理事务的时间。如果时间太长比如 IO 文件、网络、复杂数据处理等。
疑问:非UI线程发送的消息和UI绘制消息无序的在MessageQueue中排队。那么UI绘制消息处理顺序中势必会掺杂着其他消息的处理,依然会违背上面0所保证的呀?
MessageQueue中为了保证绘制UI的message消息任务及时执行处理。加入了SyncBarrier概念——同步消息处理障碍。
如图所示,插入Barrier后,其后面的同步消息被忽视,直接去顺序执行异步消息。咱们默认创建的Message都是同步的。异步消息在View绘制或者变化时候由系统创建并插入Barrier。以保证UI绘制的及时处理。
二,创建消息发送和处理Handler。
Handler 中常用的函数:Post系列(主要是给Message传入Runnable可执行体)post(Runnable r)postAtTime(Runnable r, long uptimeMillis)postDelayed(Runnable r, long delayMillis)postAtFrontOfQueue(Runnable r) //一般不建议用,容易打破消息队列执行顺序Send系列(发送消息)sendEmptyMessage(int what) //仅预示着某个节点到达的通知sendEmptyMessageAtTime(int what, long uptimeMillis)sendMessageDelayed(Message msg, long delayMillis) //可以做循环打点sendMessageAtTime(Message msg, long uptimeMillis)sendMessageAtFrontOfQueue(Message msg)Remove系列 //移除未处理又不希望被处理的消息obtain消息系列 // 调用Message.obtain()获得消息对象池的空闲消息。uptimeMillis:指的是在这个时间点被执行 delayMillis:在当前uptimeMillis基础上的uptimeMillis+ delayMillis时间点被执行。
处理函数:在Looper循环中一直查询MessageQueue的next消息msg。如果不为空则调用:
try { msg.target.dispatchMessage(msg);} finally { if (traceTag != 0) { Trace.traceEnd(traceTag); }}
这里的target就是发送这个Message的Handler。
public void dispatchMessage(Message msg) { if (msg.callback != null) { //创建消息时候传入的处理Runnable handleCallback(msg); } else { if (mCallback != null) { //一般是自己定义的处理接口 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); //handler 自己的 要我们实现的处理接口 } }
三:Looper 的loop()
在线程中启动了Looper.loop()之后。线程就进入了消息处理或者等待的模型中。
主要干了下面的事情(代码中Trace工作被去掉了):
在死循环中不停地去向MessageQueue去要下一个消息,并调用消息的Handler处理这个消息。在拿下一个消息的时候有可能被阻塞掉(在MessageQueue的next内循环)。
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked();}
Message中做的事情是不停地去遍历Message队列。这里的Message队列是通过message中添加了Message类型的 next串联起来的message链。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; //这个是Native MessageQueue 的指针 if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //通知 Native中的MessageQueue下次遍历时间 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { //遇到一个同步消息障碍 // Stalled by a barrier. Find the next asynchronous message in the queue. do {//遍历获得一个异步消息 一般是UI绘制消息 prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { //1 找到异步消息 2 第一个出队的就是同步消息 if (now < msg.when) {//还没有到处理这个消息的时间点 // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);//重置下次Poll的时间 } else { // Got a message. mBlocked = false; if (prevMsg != null) {//摘出来异步消息 prevMsg.next = msg.next; } else { //直接出队同步消息 mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) {// UI线程中不允许推出 非UI线程中分为安全退出(处理完当前消息)和强制立马推出。 dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true;//looper loop中的阻塞在这里是循环内等待消息 continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; }}
画个简约图看一下Message MessageQueue Looper Handler的关系:
最后:
**1 合理利用消息驱动机制处理UI线程中费时操作
2 合理封装框架处理自己的Handler 和 自定义Message
3 合理在非UI线程中创建UI线程的Handler。
4 这个消息驱动机制 虽然我们接触时切入点在了UI线程上。但是不要一想到UI线程就想到这个机制就是为其服务的。抛开UI线程,这个机制依然自己玩的转。在系统的后台应用中大量使用。即利用了这个消息驱动的 多线程之间传递消息并相互协作的特性。*
- Android消息驱动模式
- 【消息通信】Android消息驱动机制
- Command模式,消息驱动与模块化设计
- 设计模式学习之消息驱动机制
- Android 平台是消息驱动之Handler
- 消息驱动
- 消息驱动
- [推荐]Android消息处理机制(Handler、Looper、MessageQueue与Message) Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息
- Android消息机制-深入理解消息队列的工作模式
- Android消息驱动机制Handler+Looper+Thread+MessageQueue
- Android消息驱动机制Handler+Looper+Thread+MessageQueue
- Android消息驱动机制Handler+Looper+Thread+MessageQueue
- Android wifi驱动之earlysuspend睡眠模式
- 事件驱动VS消息驱动
- 消息模式
- 消息模式
- 使用消息驱动Beans
- 消息驱动bean
- 山东省第八届 ACM 省赛 quadratic equation
- POJ 3186 Treat for the Cows(区间dp)
- Unity UI模块优化(2.优化渲染开销)
- 【牛腩】“/”应用程序中的服务器错误
- viterbi算法 结合中文分词
- Android消息驱动模式
- 统计同成绩学生人数
- noip模拟赛 回文图
- hdu4407(容斥原理+分解质因数)Sum
- 文章标题
- HDU
- 【WineHQ】卸载已安装的软件
- 设计模式学习笔记
- 最高的奖励 51Nod