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线程,这个机制依然自己玩的转。在系统的后台应用中大量使用。即利用了这个消息驱动的 多线程之间传递消息并相互协作的特性。*