目录

前言

一、Looper轮询器

二、Handler处理者

三、MessageQueue消息队列

四、Message消息载体

五、小结

面试题:


前言

Handler机制是Android中非常重要的技术点,几乎所有的面试都会涉及到这个问题,那么为什么说它这么重要呢?因为Handler机制几乎贯穿了整个应用从启动到结束,包括Throwable抛出异常、View绘制、事件分发、Activity启动、Activity生命周期等都涉及到Handler机制。

我们知道,主线程中不建议耗时操作,子线程中不允许更新UI,所以一般我们在开发中都会Handler机制来保证线程安全。Handle运行依赖于Looper、MessageQueue和Message ,整体流程如下:

在应用启动时会开启一个主线程,默认会创建一个Looper对象关联当前线程,且系统规定一个线程只允许创建一个Looper实例对象。另外,创建Looper对象同时实例化了一个MessageQueue对象,因此一个线程中对应唯一的Looper对象和MessageQueue对象。主线程中完成Looper对象创建后会调用Looper.loop()方法,进行消息循环,不断地获取MessageQueue中的Message对象,然后调用与Message绑定的Handler对象的dispatchMessage方法实现接口回调,从而更新handle所在的主线程的UI。

流程图如下:

 

一、Looper轮询器

Looper是Handler机制运行工作的核心,所以我们先搞懂Looper是怎么回事很关键。我们知道应用的启动通过main方法作为入口,这个main方法在主线程中,查看这个类的main方法可以看到有两个方法:

Looper.prepareMainLooper()、Looper.loop()。

public static final void main(String[] args) {        ...        //主线程中调用Looper.prepareMainLooper()方法创建Looper        Looper.prepareMainLooper();        if (sMainThreadHandler == null) {            sMainThreadHandler = new Handler();        }        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        //主线程中调用Looper.loop(),开始轮询,取消息        Looper.loop();       ...    }}

首先,ActivityThread首先调用的Looper.prepareMainLooper()方法,我们来看一下这个方法源码都做了些什么?

在Looper类中public static final void prepareMainLooper() {     prepare();     setMainLooper(myLooper());     if (Process.supportsProcesses()) {        myLooper().mQueue.mQuitAllowed = false;     }}

原来,该方法中首先调用本类中的prepare()方法创建Looper对象,并且把该对象setMainLooper()绑定到当前线程中。在这里既然是主线程调用的,那么该Looper对象自然是在主线程当中。继续看里面的prepare()这个方法:

在Looper类中public static final void prepare() {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    //把Looper绑定到当前线程    sThreadLocal.set(new Looper());}

这个方法只做了一个事情,就是保证当前线程只能存在一个Looper对象,如果已经存在就会抛出异常(Android规定一个线程只允许关联一个Looper)。

继续看new Looper()这个构造方法,我们可以看到:

private Looper() {    mQueue = new MessageQueue();    mRun = true;    mThread = Thread.currentThread();}

Looper这个类的构造方法是private私有的,不允许外界直接new出来Looper对象,而最重要的是:在创建Looper的时候,同时创建了一个消息队列MessageQueue。所以请注意!MessageQueue是依赖Looper一起创建的。

然后,看ActivityThread调用Looper.loop()方法,开始轮询消息:

   public static final void loop() {        Looper me = myLooper();        //通过Looper对象,获取MessageQueue        MessageQueue queue = me.mQueue;       ...        //死循环获取消息        while (true) {            //调用MessageQueue的next()方法取消息,这个过程也是死循环            Message msg = queue.next();             if (msg != null) {                ...                //取到消息之后,交给发送该消息的Handler取处理消息                msg.target.dispatchMessage(msg);               ...                //回收消息,在Message中维护的有一个消息池                msg.recycle();            }        }    }

从上述源码中简单的注解,我们可以直观明白主线程的Looper和对应的MessageQueue之间最直接的关系了。但是我们带着一个问题如何把Message消息从子线程交给Handler在主线程中处理的?继续分析msg.target.dispatchMessage()源码:

public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {//注意这个方法调用                return;            }        }        handleMessage(msg);    }}

我们可以知道Looper.loop()方法里的死循环是不断地获取MessageQueue中的Message,然后调用与Message绑定的Handler对象的dispatchMessage方法。最后,我们终于明白原来Handler的handleMessage就在这里调用的,其实就是一个接口回调啊。

 

二、Handler处理者

我们知道Handler的工作主要就是发送和处理消息。其实Handler的构造方法有多种,但都会获取当前线程的Looper对象和MessageQueue对象。这个其实也没什么,看看就好:

//Handler的构造方法1public Handler() {    ...    //获取当前线程的Looper对象    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    //获取消息队列对象    mQueue = mLooper.mQueue;    mCallback = null;}//Handler的构造方法2public Handler(Callback callback) {   ...    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    mQueue = mLooper.mQueue;    mCallback = callback;}//Handler的构造方法3public Handler(Looper looper) {    mLooper = looper;    mQueue = looper.mQueue;    mCallback = null;}//Handler的构造方法4public Handler(Looper looper, Callback callback) {    mLooper = looper;    mQueue = looper.mQueue;    mCallback = callback;}

发送消息可以通过一系列send方法,也可以通过一系列post方法,不过post方法最终还是通过send方法去实现。
用send方法,最终也会调用一个方法如下:

//在Handler中public boolean sendMessageAtTime(Message msg, long uptimeMillis){    boolean sent = false;    MessageQueue queue = mQueue;    if (queue != null) {        //调用msg.target的方法把Message和发送它的Handler绑定,所以Looper能够把正确的Message交给发送它的Handler处理        msg.target = this;        //调用MessageQueue的enqueueMessage方法把消息加入消息队列        sent = queue.enqueueMessage(msg, uptimeMillis);    }    else {        RuntimeException e = new RuntimeException(            this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);    }    return sent;}

可以看到,Handler发送消息的过程就是向消息队列中插入一条消息。前面已经讲到,MessageQueue调用next方法去轮询消息,那么当MessageQueue拿到消息之后,把消息传递给Looper,最终交给Handler去处理,即dispatchMessage方法会被调用。此时,Handler开始处理消息。值得一提的是,在消息队列中可能有不同Handler发送的多个消息,通过在发送消息的时候把Message和发送它的Handler绑定,Looper就会把消息正确的交给发送它的Handler来处理。dispatchMessage方法如下:

//Handler中public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}

Handler处理消息的过程:

第一步,查看Message的callback是否为null,不为null就通过handleCallback(msg)方法处理消息。这里的callback实际上就是一个Runnable对象。如果以post方式去发送消息,最终就会调用handleCallback(msg)方法去处理,这个方法内容为:

 private final void handleCallback(Message message) {     message.callback.run(); }

第二步,检查mCallBack是否为null,如果不为null就调用mCallBack的handleMessage(msg)方法。这个mCallBack是CallBack接口的子类对象,前面已经说过Handler的构造方法中有两个可以用到这个CallBack。如果mCallBack为null,最终就会调用Handler的handleMessage(msg)方法,这个方法通常是在创建Handler时被使用者重写的方法。


需要说明:在主线程当前我们创建Handler时并没有自己创建Looper对象,这是因为主线程已经为我们创建好了;如果要在子线程当前创建Handler,一定要在之前创建Looper对象,即调用Looper.prepare()方法。

 

三、MessageQueue消息队列

前面的内容已经讲了很多关于MessageQueue的东西,这里就总结下了。MessageQueue主要包含两个操作:插入消息(enqueueMessage方法)和读取消息(next方法)。读取的过程也伴随着删除操作。MessageQueue的的内部实际上是通过一个单链表的数据结构来维护消息列表,这主要也是因为单链表在插入和删除上比较有优势。

   final boolean enqueueMessage(Message msg, long when) {    ...        Message p = mMessages;        if (p == null || when == 0 || when < p.when) {            // 当前发送的message需要马上被处理调,needWake唤醒状态置true            msg.next = p;            mMessages = msg;            needWake = mBlocked; // new head, might need to wake up         } else {              // 当前发送的message被排队到其他message的后面,needWake唤醒状态置为false                Message prev = null;                while (p != null && p.when <= when) {                    prev = p;                    p = p.next;                }                msg.next = prev.next;                prev.next = msg;                needWake = false; // still waiting on head, no need to wake up            }    }    // 是否唤醒主线程    if (needWake) {        nativeWake(mPtr);    }    return true;}

enqueueMessage的主要操作其实就是单链表的插入操作,根据时间看当前发送的Message是否需要马上处理。这个enqueueMessage方法是Handler发送消息的时候调用。

下面来看next方法:

final Message next() {    int pendingIdleHandlerCount = -1; // -1 only during first iteration    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        nativePollOnce(mPtr, nextPollTimeoutMillis);        synchronized (this) {            // Try to retrieve the next message.  Return if found.            final long now = SystemClock.uptimeMillis();            final Message msg = mMessages;            if (msg != null) {                final long when = msg.when;                if (now >= when) {                    mBlocked = false;                    mMessages = msg.next;                    msg.next = null;                    if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);                    return msg;                } else {                    nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);                }            } else {                nextPollTimeoutMillis = -1;            }        ...}

注意"return msg",是next方法返回一个Message对象。next方法中也是采用阻塞的方式去获取消息队列中的消息,一旦有消息立即返回并且将它从单链表中移除。如果没有消息就一直阻塞。前面已经提到,这个取消息的next方法是在Looper的loop()方法中调用。

 

四、Message消息载体

Message只有一个无参构造方法,但是Message有多个obtain静态方法来返回Message对象。

采用哪种方式创建Message对象都可以,但是建议采用obtain方法来创建。这是因为Message通过在内部构建一个链表来维护一个被回收的Message对象池。当用户调用obtain方法时会优先从池中获取,如果池中没有则创建新的Message对象。同时在使用完毕之后,进入池中以便于复用。这个在Looper.loop()方法可以看到一点端倪,在使用完毕时候调用了Message的recycle()方法。

下面是obtain方法创建Message对象的流程:

public static Message obtain() {    synchronized (sPoolSync) {        if (sPool != null) {            Message m = sPool;            sPool = m.next;            m.next = null;            sPoolSize--;            return m;        }    }    return new Message();}

 

五、小结

  • 在应用启动时会开启一个主线程,默认创建一个Looper对象绑定当前线程,且不同线程间的Looper不能共享。
  • 一个Thread线程对应一个Looper对象、对应一个MessageQueue对象。
  • 主线程中调用Looper的loop()方法,会开启消息循环,不断的从消息队列中拿到消息。
  • Looper拿到消息之后调用Handler的dispatchMessage方法来处理消息。
  • 一个Thread线程对应多个Handler。
  • Handler通过send、post方法,把消息加入消息队列MessageQueue中。
  • 子线程中创建Handler需先创建Looper对象,Thread与Handler共享一个Looper与MessageQueue。

 

面试题:

Looper.loop()为什么不会阻塞主线程?

IdHandler(闲时机制)?

postDelay()的具体实现?

post()与sendMessage()区别?

使用Handler时怎么解决内存泄漏的问题的?


 

更多相关文章

  1. 关于Handler的理解
  2. Android(安卓)数据存储和文件使用案例分析
  3. ExifInterface使用,Android(安卓)2.0新增类
  4. Android透明无边框圆形进度条之最简单实现
  5. android 使用AsyncTask实现异步下载文件
  6. android通过更改hosts免优酷广告方法
  7. 如何优雅的避免android(安卓)运行时崩溃
  8. android 热修复之类加载机制
  9. Android中Intent概述及使用

随机推荐

  1. 2011.12.05(4)——— android JNI学习之三
  2. Android(安卓)动画框架代码分析
  3. Android(安卓)ProgressDialog 最佳处理方
  4. Error:(15) No resource identifier foun
  5. android执行外部程序,类似DELPHI里的EXEC
  6. android 读取,写入图片到sd卡源码
  7. Android(安卓)IPC 通讯机制源码分析 二
  8. linux下搭建android NDK开发环境
  9. 在 Android(安卓)上使用 XML
  10. 【30篇突击 android】源码统计八