美文网首页源码Fucking source code。移动开发
撸源码系列之Handler的朋友圈

撸源码系列之Handler的朋友圈

作者: itcayman | 来源:发表于2016-07-27 18:20 被阅读213次

前言

相信做android开发的肯定经常接触Handler、Looper、MessageQueue、Message、HandlerThread等,本人也是,对这几个东西用的是非常频繁,但是在思想上还没有真正理解。趁着有时间好好撸下源码,做到用则知其所以然,然后尽可能地用自己的语言来重新温故自己的理解~。这里先抛出几个问题,
1、为什么要用Handler,我们真的了解Handler吗?
2、Handler、Looper、MessageQueue、Message、HandlerThread它们之间有着什么联系?
3、在Activity中使用Handler初始化时,可以用Handler(Looper looper),可不可以用默认的Handler(),为什么,两者有木有区别?

如何使用

我就先不讲Handler的概念了,首先我们来看一看Traning上的一个例子Task解析图片数据然后使用Handler把图片呈现到UI中,通过这个例子再结合源码分析,

public class PhotoManager {
    private PhotoManager() {
    ...
    // 定义一个附属于UI线程的Handler(这里可以思考下问题3)   
    mHandler = new Handler(Looper.getMainLooper()) {   
    ...
    /* 
     * handleMessage() 定义了当Handler接收到新消息的处理过程.        
     */        
    @Override        
    public void handleMessage(Message inputMessage) {            
        // 从Message对象中获取image task      
        PhotoTask photoTask = (PhotoTask) inputMessage.obj;    
        ...       
        // Gets the ImageView for this task                    
        PhotoView localView = photoTask.getPhotoView();                    
        ...                    
        switch (inputMessage.what) {                        
        ...                    
            // 解析过程完成                       
            case TASK_COMPLETE:                            
               /*                            
                * 把 Bitmap从task显示在View上                           
                */
                localView.setImageBitmap(photoTask.getImage());
                break;                        
            ...                                        
        } 
    }    
       
    // 处理task的状态信息
    public void handleState(PhotoTask photoTask, int state) {        
        switch (state) {            
        ...            
            // task完成下载和解析image            
            case TASK_COMPLETE:                
                /*                 
                 * 创建一个带有状态和task对象的message发送给handler                
                 */                
                Message completeMessage = mHandler.obtainMessage(state, photoTask);                
                completeMessage.sendToTarget();                
                break;            
            ...        
        }        
        ...   
    }
}

首先定义了一个Handler,注意这里传入了一个Looper.getMainLooper(),这里可以联系到问题3。然后handleMessage(Message inputMessage)方法处理传过来的message实例,根据message的what值来决定下一步该执行的操作。我们继续看PhotoDecodeRunnable类:

// A class that decodes photo files into Bitmaps
class PhotoDecodeRunnable implements Runnable {   
    ...    
    PhotoDecodeRunnable(PhotoTask downloadTask) {        
        mPhotoTask = downloadTask;    
    }    
    ...    
    // Gets the downloaded byte array    
    byte[] imageBuffer = mPhotoTask.getByteBuffer();    
    ...    
    // Runs the code for this task    
    public void run() {       
        ...        
        // Tries to decode the image buffer        
        returnBitmap = BitmapFactory.decodeByteArray(imageBuffer, 0, imageBuffer.length, bitmapOptions);        
        ...        
        // Sets the ImageView Bitmap      
        mPhotoTask.setImage(returnBitmap);        
        // Reports a status of "completed" 
        mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);       
        ...   
    }    
    ...
}

PhotoDecodeRunnable主要是从task中拿到byte数据,然后解析转换为Bitmap,再把引用传递给task,最后让task执行解析数据完成的操作。

public class PhotoTask {    
    ...    
    // Gets a handle to the object that creates the thread pools 
    sPhotoManager = PhotoManager.getInstance();   
     ...    
    public void handleDecodeState(int state) {        
        int outState;        
        // Converts the decode state to the overall state.        
        switch(state) {            
            case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:                              outState = PhotoManager.TASK_COMPLETE;                
                break;            
            ...        
        }        
        ...        
        // Calls the generalized state method        
        handleState(outState);    
    }    
    ...    
    // Passes the state to PhotoManager    
    void handleState(int state) {        
        /*         
         * Passes a handle to this task and the current state to the class that     created the thread pools         
         */        
        sPhotoManager.handleState(this, state);    
    }    
    ...
}

可以看到sPhotoManager.handleState(this, state);这句代码最后还是会调用PhotoManager中的handleState方法:

Message completeMessage = mHandler.obtainMessage(state, photoTask);                
completeMessage.sendToTarget();  

当Handler接受到消息时就会执行handleMessage方法了,以上只是一个简单的Handler使用场景,这里就不再做描述。。。

Handler+Looper+Message

接下来开始分析源码,从哪开始呢????我觉得还是先从Handler下手合理点,毕竟在我们使用阶段最开始初始化的是它,然后探索源码的过程中穿插着结合其它相关类一起分析。
一般在Activity中使用Handler,我们会直接new Handler();然而上面这个例子却使用Handler(Looper looper)构造函数,这两个想必肯定跟Looper有千丝万缕的关系。来看Handler类:

public Handler() {    
    this(null, false);
}
...
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;
}

这里有句mLooper = Looper.myLooper(); 貌似很关键,俺情不自禁地点了下来到Looper类中,

public final class Looper {
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    ...
    private Looper(boolean quitAllowed) {   
        //我们要知道这里初始化了一个MessageQueue用于存放消息,后面再细讲    
        mQueue = new MessageQueue(quitAllowed); 
        mThread = Thread.currentThread();
    }
    ...
    public static Looper myLooper() {    
        return sThreadLocal.get();
    }
    ...
    public static void prepare() {    
        prepare(true);
    }
    ...
    /*
    * Initialize the current thread as a looper. 
    * This gives you a chance to create handlers that then reference 
    * this looper, before actually starting the loop. Be sure to call 
    * {@link #loop()} after calling this method, and end it by calling 
    * {@link #quit()}. 
    */
    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));
    }

    /** 
    * 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();    
        }
    }
}

这里ThreadLocal保证每个线程只存在一个Looper对象,对ThreadLocal不了解的可以看理解Java中的ThreadLocal(这篇文章讲的是JDK1.5的,虽然有点老,凑合着看)。可以看到提供了prepare()方法来new一个新的Looper对象,然后把这个对象set到ThreadLocal中。再接着看这个prepareMainLooper()这个方法,注释写着当调用这个方法时,调用者所在的当前线程会被当作application的主线程使用,主线程会被Android环境创建,开发者不能擅自调用这个方法,所以大家不要抱着好玩的心态随笔调用~_~,好了,到了这里,大家对开始提到的问题3有木有印象?没有印象的可以回到最上面再回顾下这个问题,再结合目前对Handler和Looper的关系自由发挥。另外机智的我们可能会提出这样一个疑问,这个prepareMainLooper()到底是谁调用了,所谓的主线程,亦即UI线程是不是这个方法的调用者所在线程?
我就喜欢这样爱提问的童鞋有木有,不废话了。当时为了搞清楚UI线程的具体情况,我花了一段时间去研究Android的启动过程,最后在ActivityThread这个类的main方法中找到答案,激动得一晚没有睡觉有木有...好了,来看ActivityThread中的这个方法:

public final class ActivityThread {
    ...
    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");
    }
  ...
}

分析到这里,问题3的答案已经很明显了,当然是在Activity中可以使用new Handler()来创建handler。在这里我还要抛出几个问题:

  • ActivityThread并没有继承Thread,它既然不是线程,为什么它又叫做主线程或者UI线程呢?
  • 既然UI线程是ActivityThread,那Activity与ActivityThread为什么能共用一个Looper?
  • 为什么主线程不会因为Looper.loop()里的死循环卡死?

其实ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread确实没有继承Thread类,只是往往运行在主线程,给人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程,当它执行了main方法时就注定了它是主线程的命运。不信的话我们看下面这张图


另外大家可以看看ActivityThread中的performLaunchActivity方法,这个就是启动我们所有activity的方法。所以activity和ActivityThread是在同一个线程的。对这个还有疑惑的可以看Android ActivityThread(主线程或UI线程)简介,应该会有点帮助。还有一篇知乎文章也分析得很透彻,详情请见为什么主线程不会因为Looper.loop()里的死循环卡死?

好了,我们再来看段Looper源码中推荐的非UI线程中使用Handler的经典例子,

* <pre>
*  class LooperThread extends Thread {
*      public Handler mHandler;
*      public void run() {
*          Looper.prepare();
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*          Looper.loop();
*      }
*  }</pre>

这里一共有3个步骤:
1. 在线程启动后执行Looper.prepare(),new一个Looper并把这个Looper存放到ThreadLocal中
2. new一个Handler对象,并实现handleMessage方法。
3. 执行Looper.loop();启动looper循环读取消息。
接下来,我们来看看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) {            
        // 没有消息的时候退出消息队列循环.  
            return;        
        }        
        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;        
        if (logging != null) {            
            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        
        }        
        msg.target.dispatchMessage(msg);  //这句很关键     
        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);        
        }  
        //消息被handler转发处理后,message就会执行回收操作被放入缓存池      
        msg.recycleUnchecked();    
    }
}

从上面代码可以知道,调用loop()方法后,Looper就进入死循环从消息队列中读消息了,但是这里的MessageQueue中的Message是怎么来的呢?这个我们后面分析Handler的时候会讲,接下来,继续跟踪msg.target.dispatchMessage(msg);这句代码,进入Message类,发现有个类型为Handler名字为target的参数,那么这个target是怎么传进来的呢?
别急,我们继续分析,一般开发使Message中有2种用法,

  • 一种是直接new Message();
  • 还有一种是Message.obtain();
    obtain方法有几个变种不过最后都调用了无参的obtain(),不过在其基础上做了一些赋值操作。我们来看看Message里的部分方法(关键点我在代码中直接用注释说明):
public final class Message implements Parcelable {
        Bundle data;  //除了arg1,arg2,Object的话还可以传Bundle
        Handler target;  //必须有Handler来消费这个Message
        Runnable callback;  //这个在Handler处理消息时会优先调用,后面在讲Handler中会再次提到
        private static Message sPool;  //Message链表缓存
        Message next;  //指向下一个
        private static final int MAX_POOL_SIZE = 50;  //最大缓存量
        ...
        //从Message缓存中拿出,若缓存为空则new一个新的Message,
        //这个链表缓存具体实现就不详细讲了,感兴趣的童鞋自行看源码
        public static Message obtain() {    
            synchronized (sPoolSync) {        
                if (sPool != null) {            
                    Message m = sPool;            
                    sPool = m.next;            
                    m.next = null;            
                    m.flags = 0; // clear in-use flag            
                    sPoolSize--;            
                    return m;        
                }    
            }    
            return new Message();
        }
        public static Message obtain(Message orig) {    
            Message m = obtain();    
            m.what = orig.what;    
            m.arg1 = orig.arg1;    
            m.arg2 = orig.arg2;    
            m.obj = orig.obj;    
            m.replyTo = orig.replyTo;    
            m.sendingUid = orig.sendingUid;    
            if (orig.data != null) {        
                m.data = new Bundle(orig.data);    
            }    
            m.target = orig.target;    //赋值target
            m.callback = orig.callback;    
            return m;
        }
        //那几个变种方法跟上面这个方法类似都是一些赋值操作就不贴代码了
        ...
    }

Looper和Message讲了这么多,和Handler的关系应该讲清楚了,我们再回过头来看Handler,看看Handler是怎么发送和处理Message的,

public class Handler {
        ...
        final MessageQueue mQueue;//这个MessageQueue是Looper中new出来的
        final Looper mLooper;
        //这个回调是为了Handler和工作线程绑定时与Activity、Service通信
        final Callback mCallback;
        ...
        public final boolean sendMessage(Message msg){    
            return sendMessageDelayed(msg, 0);
        }
        public final boolean sendEmptyMessage(int what){    
            return sendEmptyMessageDelayed(what, 0);
        }
        public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {    
            Message msg = Message.obtain();    
            msg.what = what;    
            return sendMessageDelayed(msg, delayMillis);
        }
        public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {    
            Message msg = Message.obtain();    
            msg.what = what;    
            return sendMessageAtTime(msg, uptimeMillis);
        }
        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);
        }
        public final boolean sendMessageAtFrontOfQueue(Message msg) {
            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, 0);
        }
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
            msg.target = this;    //让Message的target指向当前Handler实例
            if (mAsynchronous) {        
                msg.setAsynchronous(true);    
            }    
            return queue.enqueueMessage(msg, uptimeMillis);
        }
        ...
        //子类实现这个方法来接收处理消息
        public void handleMessage(Message msg) {}
        //这个方法在Looper.loop()方法中会调用
        public void dispatchMessage(Message msg) {    
            if (msg.callback != null) {        
                handleCallback(msg);    
            } else {        
                if (mCallback != null) {            
                    if (mCallback.handleMessage(msg)) {                
                        return;            
                    }        
                }        
                handleMessage(msg);    
            }
        }
}

从上面可以看到,msg.target = this; 这句代码消除了之前提的Message中的target从哪里来的疑问。最后会调用queue.enqueueMessage(msg, uptimeMillis);这句代码的意思是把Message放入MessageQueue队列,之前讲Looper的时候也提到过在loop()方法中死循环从MessageQueue中取出Message给Handler去处理。关于MessageQueue具体是怎么处理Message的,由于篇幅有限,大家感兴趣的自行查看源码。
我们再来看看dispatchMessage这个方法,诶诶诶,是不是很眼熟,对了,在Looper.loop()方法中出现过一次,它的作用就是转发消息,这里有3中处理情况咱们按照从高到低的优先级来说:
** 1. 把消息交给Message中的callback处理**
** 2. 把消息交给Handler中的callback处理**
** 3. 把消息交给子类的handleMessage处理**
这几种情况后面讲Handler的使用姿势时详细讲解。接下来我们再讲讲HandlerThread:
其实,HandlerThread就是一个绑定了Looper的Thread,根据前面的分析,我们已经知道handler需要和looper绑定,而looper会存放在ThreadLocal中,只不过后面这部分工作HandlerThread已经帮我们做好了,我们只需要做前面那部分工作。
讲了这么多,Handler、Looper、Message、MessageQueue、HandlerThread的关系我们应该已经了解的差不多了,下面给出UMl图和关系图好让我们对它们印象更深:

UML图
Handler关系图

总结#

说了这么多,最后用文字描述下各自的关系吧!打个比方,UI线程(main Thread)是国王,各个工作线程是他的百姓,有很多事情百姓是不能直接去做的,就必须请求国王去处理,但是又不能每个人都亲自去见国王,所以很有必要打造一套通信体系,于是有了接下来的故事。。。(这个比方只适合理解工作线程发消息给UI线程的场景,反过来UI线程发消息给工作线程也是类似的道理)
1、 Handler
百姓(work Thread)与国王(main Thread)进行通信的通信员,因为这个国家是中央集权制,国王有重大事情的决定权(android中UI操作是单线程模型,为什么要用单线程模型呢,想想多个线程共同操作同一个View会有什么情况。。。),所以,百姓想要通知国王去做某件利国利民的事情的话就需要单独选出值得信赖的通信员来负责双方沟通,而Handler就承担了这个通信员的工作。
2、Message
有了通信员,就得有具体的信件体,从这个信件体里我们可以知道消息要发给谁(msg.target),要发什么内容(msg.what),什么时候发(msg.when),附带的物件是什么(msg.obj或着msg.data)。
3、MessageQueue
有了通信员、信件体,那当然少不了一个背包了,这个背包主要用于存放不断送过来的信件体,根据信件要到达的时间来判断信件处于的位置,这些信件的到达时间会形成一道时间轴(Queue队列)。
4、Looper
通信员很懒,他不愿去背包拿信件,于是需要一个智能的机器人来负责从背包取信件,这个机器人只会重复的去取信件(loop循环取Message)。但是这个机器人工作之前必须要做初始化准备工作(prepare操作),而且下达执行这个工作命令的是要发信件的百姓。

做android开发,弄懂Handler、Message、MessageQueue、Looper的关系是很有必要的,但是真正理解透彻的话就要下功夫研究ActivityThread、AMS、Lanucher等framework层的知识了。哈哈,还是那句话: Read the fucking source code !

相关文章

网友评论

本文标题:撸源码系列之Handler的朋友圈

本文链接:https://www.haomeiwen.com/subject/ehdbjttx.html