专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
鸿洋  ·  2024 了, Insets 你不会还不懂吧? ·  昨天  
鸿洋  ·  鸿蒙版 React Native ... ·  2 天前  
51好读  ›  专栏  ›  安卓开发精选

Android Handler 消息机制(解惑篇)下

安卓开发精选  · 公众号  · android  · 2016-10-12 09:59

正文

(点击上方公众号,可快速关注)


来源:伯乐在线专栏作者 - PleaseCallMeCoder

链接:http://android.jobbole.com/84957/

点击 → 了解如何加入专栏作者


接上文


接下来我们看一下handler是如何发送消息的


/**

* Causes the Runnable r to be added to the message queue.

* The runnable will be run on the thread to which this handler is

* attached.

*  

* @param r The Runnable that will be executed.

*

* @return Returns true if the Runnable was successfully placed in to the

*         message queue.  Returns false on failure, usually because the

*         looper processing the message queue is exiting.

*/

public final boolean post(Runnable r)

{

   return  sendMessageDelayed(getPostMessage(r), 0);

}

 

/**

* Enqueue a message into the message queue after all pending messages

* before (current time + delayMillis). You will receive it in

* {@link #handleMessage}, in the thread attached to this handler.

*  

* @return Returns true if the message was successfully placed in to the

*         message queue.  Returns false on failure, usually because the

*         looper processing the message queue is exiting.  Note that a

*         result of true does not mean the message will be processed -- if

*         the looper is quit before the delivery time of the message

*         occurs then the message will be dropped.

*/

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

    if (delayMillis 0) {

        delayMillis = 0;

    }

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

 

/**

* Enqueue a message into the message queue after all pending messages

* before the absolute time (in milliseconds) uptimeMillis.

* The time-base is {@link android.os.SystemClock#uptimeMillis}.

* Time spent in deep sleep will add an additional delay to execution.

* You will receive it in {@link #handleMessage}, in the thread attached

* to this handler.

*

* @param uptimeMillis The absolute time at which the message should be

*         delivered, using the

*         {@link android.os.SystemClock#uptimeMillis} time-base.

*        

* @return Returns true if the message was successfully placed in to the

*         message queue.  Returns false on failure, usually because the

*         looper processing the message queue is exiting.  Note that a

*         result of true does not mean the message will be processed -- if

*         the looper is quit before the delivery time of the message

*         occurs then the message will be dropped.

*/

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

    MessageQueue queue = mQueue;

    if (queue == null) {

        RuntimeException e = new RuntimeException(

                this + " sendMessageAtTime() called with no mQueue");

        Log.w("Looper", e.getMessage(), e);

        return false;

    }

    return enqueueMessage(queue, msg, uptimeMillis);

}

 

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

    msg.target = this;

    if (mAsynchronous) {

        msg.setAsynchronous(true);

    }

    return queue.enqueueMessage(msg, uptimeMillis);

}


这里我们只列出了一种调用关系,其他调用关系大同小异,我们来分析一下


  1. 调用getPostMessage(r),把runnable对象添加到一个Message对象中。

  2. sendMessageDelayed(getPostMessage(r), 0),基本没做什么操作,又继续调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在这个方法里拿到创建这个Handler对象的线程持有的MessageQueue。

  3. 调用enqueueMessage(queue, msg, uptimeMillis)方法,给msg对象的target变量赋值为当前的Handler对象,然后放入到MessageQueue。


那发送消息说完了,那我们的消息是怎样被处理的呢?


我们看到message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码。


/**

* Callback interface you can use when instantiating a Handler to avoid

* having to implement your own subclass of Handler.

*

* @param msg A {@link android.os.Message Message} object

* @return True if no further handling is desired

*/

public interface Callback {

    public boolean handleMessage(Message msg);

}

 

/**

* Subclasses must implement this to receive messages.

*/

public void handleMessage(Message msg) {

}

 

/**

* Handle system messages here.

*/

public void dispatchMessage(Message msg) {

    if (msg.callback != null) {

        handleCallback(msg);

    } else {

        if (mCallback != null) {

            if (mCallback.handleMessage(msg)) {

                return;

            }

        }

        handleMessage(msg);

    }

}

 

private static void handleCallback(Message message) {

    message.callback.run();

}


我们看到这里最终又调用到了我们重写的handleMessage(Message msg)方法来做处理子线程发来的消息或者调用handleCallback(Message message)去执行我们子线程中定义并传过来的操作。


思考


为什么要有Handler机制


这个问题可以这么考虑


  • 我们如何在子线程更新UI?——使用Handler机制传递消息到主线程(UI线程)

  • 为什么我们不在子线程更新UI呢?——因为Android是单线程模型

  • 为什么要做成单线程模型呢?——多线程并发访问UI可能会导致UI控件处于不可预期的状态。如果加锁,虽然能解决,但是缺点也很明显:1.锁机制让UI访问逻辑变得复杂;2.加锁导致效率低下。


Handler机制与命令模式


我在之前分享过Android源码中的命令模式,我们仔细分下一下不难看出Handler机制其实是一个非典型的命令模式。


  • 接收者:Handler,执行消息处理操作。

  • 调用者:Looper,调用消息的的处理方法。

  • 命令角色:Message,消息类。

  • 客户端:Thread,创建消息并绑定Handler(接受者)。


Android主线程是如何管理子线程消息的


我们知道Android上一个应用的入口,应该是ActivityThread。和普通的Java类一样,入口是一个main方法。


public static void main(String[] args) {

 

    //~省略部分无关代码~

 

    //创建Looper和MessageQueue对象,用于处理主线程的消息

    Looper.prepareMainLooper();

 

    //创建ActivityThread对象

    ActivityThread thread = new ActivityThread();

 

    //建立Binder通道 (创建新线程)

    thread.attach(false);

 

    if (sMainThreadHandler == null) {

        sMainThreadHandler = thread.getHandler();

    }

 

    if (false) {

        Looper.myLooper().setMessageLogging(new

                LogPrinter(Log.DEBUG, "ActivityThread"));

    }

 

    // End of event ActivityThreadMain.

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

 

    //消息循环运行

    Looper.loop();

 

    throw new RuntimeException("Main thread loop unexpectedly exited");

}


我们可以看到其实我们在这里初始化了我们主线程(UI)的Looper并且启动它。然后就可以处理子线程和其他组件发来的消息了。


为什么主线程不会因为Looper.loop()里的死循环卡死或者不能处理其他事务


这里涉及到的东西比较多,概括的理解是这样的


  • 为什么不会卡死handler机制是使用pipe来实现的,主线程没有消息处理时会阻塞在管道的读端。

  • binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回。


主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。


既然是死循环又如何去处理其他事务呢?答案是通过创建新线程的方式。

我们看到main方法里调用了thread.attach(false),这里便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。


ActivityThread对应的Handler是一个内部类H,里边包含了启动Activity、处理Activity生命周期等方法。


参考资料


  • http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html

  • http://czpsailer.iteye.com/blog/655942

  • https://www.zhihu.com/question/34652589


专栏作者简介( 点击 → 加入专栏作者 


PleaseCallMeCoder:我是 PleaseCallMeCoder,一个小小的90后程序员。热衷于移动开发,喜欢研究新技术,奔跑在成为大神的路上。

打赏支持作者写出更多好文章,谢谢!



 关注「安卓开发精选

看更多精选安卓技术文章
↓↓↓