• 源码分析Android Handler是如何实现线程间通信的


    源码分析Android Handler是如何实现线程间通信的

    Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的。开发者从一开始就被告知必须在主线程中进行UI操作。但Handler是如何实现线程间通信的呢?本文将从源码中分析Handler的消息通信机制。

    0x00 Handler使用

    首先看看我们平时是如何使用的Handler的。先看看以下代码

    //定义Handler
    Handler mHandler = new Handler(){
      public void handleMessage(Message msg){
        switch(msg.what){
          case UPDATE_UI:
            updateUI(msg);
            break;
        }
      }
    };
    class MyThread extends Thread{
      public void run(){
        //do same work!
        ...
        //send message
        Message msg = mHandler.obtainMessage(UPDATE_UI);
        mHandler.sendMessage(msg);
      }
    }
    
    private void updateUI(Message msg){
      //update UI
    }
    

    在子线程中sendMessage(Message)发送消息,然后在Handler的handleMessage(Message)接收消息,执行更新UI操作。那么Handler是如何把消息从MyThread传递到MainThread中来呢?我们从sendMessage()开始慢慢揭开它的面纱。

    0x01 sendMessage(Message)

    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    ...
    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
          delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    ...
    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);
    }
    

    我们发现调用sendMessage()方法最后都走到enqueueMessage()这个方法,一开始就把当前Handler实例赋给了Message.target的属性里面,后面可以知道这个target是用来执行处理函数回调的。

    enqueueMessage方法是把Message信息放入到一个MessageQueue的队列中。顾名思义MessageQueue就是消息队列。从sendMessageAtTime()方法知道这个MessageQueueHandler中的一个成员。它是在Handler的构造函数中通过Loopger对象来初始化的。

    0x02 Handler构造函数

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
    
        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;
        mAsynchronous = async;
    }
    

    这时候我们脑海知道创建Handler的时候,同时也创建了Looper实例和MessageQueue引用(MessageQueue对象其实是在Looper中构造的)。Looper是何物呢?简单地说就是消息循环,这个我们稍后会分析。

    0x03 enqueueMessage(MessageQueue)

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
    
        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
    
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                  //这里把消息插入到队列中
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
    
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

    MessageQueue中可以看到这个入列方法中有一个for循环就是把当前的需要处理Message放到队列的合适位置。因为需要处理的Message对象都有一个开始处理的时间when,这个队列是按照when排序的。

    至此,Handler调用sendMessage()方法后就把Message消息通过enqueueMessage()插入MessageQueue队列中。

    而这个MessageQueue是在Looper中维护的。

    0x04 prepare()创建Looper

    0x02中我们知道创建Handler时就使用静态方法Looper.myLooper()得到当前线程的Looper对象。

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    sThreadLocal是一个ThreadLocal类型的静态变量。什么时候会把Looper对象放在sThreadLocal中呢?通过prepare()方法。

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    

    继续翻阅源码知道Looper在构造函数中创建MessageQueue对象

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    调用prepare()方法将一个Looper对象放在了静态的ThreadLocal对象中。这个是一个与线程绑定的对象,且在内存中仅保存了一份引用。

    使用ThreadLocal对象这一点非常巧妙,也非常重要,这是线程间通信的基础。即在线程中调用prepare()时就在该线程中绑定了Looper对象,而Looper对象中拥有MessageQueue引用。所以每个线程都有一个消息队列

    这样HandlerLooperMessageQueue这几个类关系大概就可以画出来了。

    Handler类图

    0x05 启动循环loop()

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
    
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //这里执行消息队列循环
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
    
            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
    
            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
              //执行处理消息的回调
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
    
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
    
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
    
            msg.recycleUnchecked();
        }
    }
    

    loop()方法中有一个无限循环,不停地读取调用MessageQueuenext()方法。当next()没有返回时就阻塞在这里。当获取到MessageQueue中的消息时,就执行了处理消息的回调函数msg.target.dispatchMessage(msg)

    前面0x01分析我们知道msg.target是在Handler中的enqueueMessage()进行赋值,即它指向当前的Handler实例。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    执行msg.target.dispatchMessage(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);
        }
    }
    

    这里就是回调handleMessage(msg)函数处理消息的地方。Handler负责将Message入列,Looper则负责循环从MessageQueue中取出需要处理的Message并交由Handler来处理。

    0x06 启动主线程的消息循环

    我们知道通过静态方法Looper.prepare()创建了绑定当前线程的Looper对象,而通过loop()启动一个循环不停地读取队列中Message。但是Android系统是什么时候启动了主线程的消息循环呢?

    要理解这一点就必须进入Android应用程序的入口ActivityThreadmain方法。

    public static void main(String[] args) {
        ...
    
        Looper.prepareMainLooper();
    
        ...
        Looper.loop();
    
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    可以看出main方法中先后执行了Looper.prepareMainLooper()方法和Looper.loop()方法。正常情况下main方法不会退出,只有loop()方法发生异常后将会抛出RuntimeException

    0x07 Looper.prepareMainLooper()

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    prepareMainLooper()方法其实是调用了prepare()方法。

    当我们启动应用时系统就调用了prepareMainLooper()并在主线程中绑定了一个Looper对象。

    这时候我们回过来看看一开始的Handler使用方式。在主线程中我们创建了Handler对象,在Handler构造函数中初始化了Looper(即获取到了绑定在主线程中的Looper对象)。当在子线程MyThread中通过mHandler.sendMessage(msg)方法发送一个消息时就把Message放在与主线程绑定的MessageQueue中。这样在子线程中使用Handler就实现了消息的通信。

    可以简单的使用以下类图表示,每个线程都由一个Handler,每个Handler都是与当前所在线程的Looper绑定

    线程Handler模型

    0x08 主线程是否会阻塞

    0x06中知道在ActivityTheadmain方法中启动了一个死循环。那主线程是不是就一直阻塞在这里呢?其实不然。可以看到ActivityThread类里面有一个自定义的Handler对象mH,在这里对象中handleMessage()回调中定义了Activity的各种交互如管理Activity生命周期,启动service,显示window等,都是通过Handler进行处理的。同时可以看出只有当应用退出EXIT_APPLICATION之后才回调用Looper.quit()停止消息循环。

    public void handleMessage(Message msg) {
        ...
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                ...
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            ...
            case PAUSE_ACTIVITY: {
                ...
                handlePauseActivity((IBinder) args.arg1, false,
                        (args.argi1 & USER_LEAVING) != 0, args.argi2,
                        (args.argi1 & DONT_REPORT) != 0, args.argi3);
                ...
            } break;
    
            ...
            case SHOW_WINDOW:
                ...
                handleWindowVisibility((IBinder)msg.obj, true);
                ...
                break;
            ...
            case EXIT_APPLICATION:
                if (mInitialApplication != null) {
                    mInitialApplication.onTerminate();
                }
                Looper.myLooper().quit();
                break;
            ...
        }
        ...
    }
    

    0x09 总结

    当创建Handler时将通过ThreadLocal在当前线程绑定一个Looper对象,而Looper持有MessageQueue对象。执行Handler.sendMessage(Message)方法将一个待处理的Message插入到MessageQueue中,这时候通过Looper.loop()方法获取到队列中Message,然后再交由Handler.handleMessage(Message)来处理。

    微信关注我们,可以获取更多

  • 相关阅读:
    AChartEngine方法的使用及事件汇总
    免费的Android UI库及组件推荐
    消灭Bug!十款免费移动应用测试框架推荐
    AChartEngine 安卓折线图 柱形图等利器
    添加几个有用的网址
    演示Android百度地图操作功能
    Android 如何在Eclipse中查看Android API源码 及 support包源码
    Android入门之GPS定位详解
    软考之数据库部分
    SSE && WebSockets
  • 原文地址:https://www.cnblogs.com/angrycode/p/6576905.html
Copyright © 2020-2023  润新知