Android消息驱动模式

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值