在android开发过程中相信屌丝程序员们都用过Handler来处理一些逻辑任务,比如发送延迟消息处理业务等逻辑,我们常用的图片加载缓存库ImageLoader和Picasso等内部也是通过Handler来最终有后台加载线程切换到主线程(UI线程)来更新页面的,今天就趁着离职有点儿时间就抽空的分析了下它的一点源码,在此总结出来。闲言少叙,书归正传!

先来谈谈Looper:

Looper从源码上来看就是一个普通的Java类,它在消息机制中,顾名思义,就是一个消息循环的角色。下面让我们看看Looper的构造器都做了什么:

//(每个Looper对象的)消息队列,也就是说每个Looper对象都持有自己的消息队列 final MessageQueue mQueue;//(每个Looper线程关联的)当前线程final Thread mThread;private Looper(boolean quitAllowed) {    //初始化当前Looper对象的消息队列        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();//获取当前线程    }

从上面的代码中我们可以得出如下简单的结论:
a. <每个Looper对象都有自己的消息队列MessageQueue!
b. 每个Looper对象都和当前线程或者说创建Looper的线程相关联
那么问题来了,当前线程是如何跟Looper对象想关联的呢?如果你读过Looper源码,从代码注释中你可以看到下面一个代码:

  class LooperThread extends Thread {        public Handler mHandler;        public void run() {            //注意是在run方法中调用了prepare            Looper.prepare();            mHandler = new Handler() {                public void handleMessage(Message msg) {                }            };            Looper.loop();        }    }

可以发现里面调用了prepare()这个静态方法,所以直接看看prepare()这个方法做了什么了不起的事儿!

//注意为静态final变量static final ThreadLocal sThreadLocal = new ThreadLocal(); public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        //一个Thread只能关联一个Looper对象        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));    }

从代码上不难看出prepare做了两个工作:
a.初始化Looper对象:在当前线程中创建一个Looper对象,放入ThreadLocal中;ThreadLocal作用简单来说就是在每个线程中存储数据,每个线程只能获取到自己存储在ThreadLocal的数据,其他的线程是获取不到自己线程存储在ThreadLocal的数据的。当然既然用ThreadLocal保存一个Looper那么我们肯定可以通过ThreadLocal得到这个Looper对象,方法如下:

//获取当前线程关联的Looper对象public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

b.如果当前的线程已经有一个Looper对象相关联,就会抛出异常,也 就是说一个Thread只能关联一个Looper对象

总之一句话prepare()方法就是让线程关联Looper对象用的

写到此处不难发现Looper已经完成了如下工作:

这样我们线程也绑定Looper了,消息队列也由Looper对象创建好了,所以该是消息队列工作的时候了!!!那怎么才能让一个Looper对象持有的消息队列工作呢?其实笼统的说一句无非就是不停的从消息队列中查看是否有新的消息,有则处理,无则阻塞!这个处理消息的方法其实在上面的例子中也现身过,那就是loop()这个静态方法!

  /**该方法必须在prepare方法之后调用**/  public static void loop() {    //获取当前线程锁关联的Looper对象        final Looper me = myLooper();        if (me == null) {//在调用loop()之前必须调用looper.prepare()方法            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }//获取当前对象的消息队列        final MessageQueue queue = me.mQueue;        ..........省略两行代码......        for (;;) {//是一个无限循环,来遍历消息队列    //获取一条消息Message对象,可能会造成阻塞,            Message msg = queue.next(); // might block            if (msg == null) {                return;            }            //该消息的target是一个Handler,来分发消息,具体怎么分发稍后讨论            msg.target.dispatchMessage(msg);           .....省略部分代码..            msg.recycleUnchecked();        }    }

可以发现loop()方法其实执行了如下两步工作:
1)获取当前线程关联的Looper对象
2)获取Looper对象的消息队列然后开启无限循环获取消息和处理消息
重点就是第二步了,在无限循环中有且只有一个跳出的入口:那就是消息队列的next方法返回了null!另外需要注意的是next方法是一个阻塞方法,这也意味着当MessQueue没有消息的时候,next方法会阻塞进而使得loop方法也一直阻塞。当然next方法有新的消息的时候就调用 msg.target.dispatchMessage(msg);发送并处理消息!需要注意的消息队列中的Mssage都有自己的target对象来处理,target对象不唯一!。简单的概况下可以用如下流程图简单表示:

通过上面的流程图也可以清晰的知道在获取到一个Messge对象之后,通过Message.target.dispatchMessage进行消息的分发和处理。那么这个target到底值什么鬼呢?还记得上文的例子代码LooperThread么?里面有个handler是什么意思呢会不会就这这个target呢?其实回答这个问题之前或许我们应该考虑:“我们是什么时候向Looper中的消息队列添加一条条消息来供loop循环读取呢?“
这就不得不说本篇博文的另一个主角Handler了,顺便说一句,Message的target你应该能想到其实也是一个Handler对象,不信?后面又说明!

Handler简析

先看看Handler的一个构造器,为了说明问题捡了其中的一个构造函数来说明:

//当前线程关联Looper对象的消息队列 final MessageQueue mQueue; //当前线程关联的Looper对象  final Looper mLooper;public Handler(Callback callback, boolean async) {        ...省略部分代码..//获取当前线程中关联的的Looper对象        mLooper = Looper.myLooper();        if (mLooper == null) {/不能在Thread里面创建Hanlder对象,如果没有调用prepare的话)            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }//这个就是初始化关联对象的地方        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

从上面的代码中可以看出一个Handler对象有如下信息:
1)Handler对象持有一个Looper对象的引用mLooper
2)Handler对象持有一个消息队列对象的引用mQueue ,并且该引用在构造器中得到了初始化,初始化也很简单就是把looper对象创建的消息队列MessageQueue赋值给mQueue对象。
也就是说Handler关联了Looper对象及Looper对象创建的消息队列
3)在一个子线程里面是如果没有调用Looper.prepare,不能创建Handler对象!
万事俱备,是时候回答“什么时候向消息队列添加消息”这个问题了!
在使用Handler的时候我们是通过sendMessage方法发送消息的,看看这个方法都做了什么,查看其源码,它的调用脉络是:
sendMessage(Message)–>sendMessageDelayed(Message, long)–>sendMessageAtTime(Message,long);所以直接看sendMessageAtTime这个方法即可:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        ****省略了部分代码**        return enqueueMessage(queue, msg, uptimeMillis);    }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        //初始化了target从这里可以看出来target就是一个Handler        msg.target = this;       //调用messageQueue的enqueueMessage插入消息        return queue.enqueueMessage(msg, uptimeMillis);    }

最终sendMessageAtTime回调用enqueueMessage这个方法,这个方法可以得到如下结论:
a,上图中loop()循环取的message对象后调用message.target,这个target就是一个Handler!
b.Handler调用sendMessage方法发送消息的过程其实就是向MessageQueue这个消息队列插入一条消息的过程,另外我们到这里可以做出以下断言:在UI线程中(主线程)中创建的Handler对象通过sendMessage发送的Message实际上是添加到了UI线程的消息队列中!

其实写到此处还可以得到一个结论:同一个线程中创建的多个Handler对象共享一个Looper对象,当然也就共享一个MessageQueue队列。换句话说同一个线程中的若干Handler对象发送的message消息都会发到一个消息队列中去,每个message对象根据其target属性来决定自己是哪一个handler发来的,可以简单用如下图来表示:


Looper的loop()方法通过循环消息队列,通过其next方法获取消息,然后处理之。所以下面就该讨论处理流程了。

很简单就从上图中的dispatchMessage方法说起:

 public void dispatchMessage(Message msg) {        //如果创建的msg,设置了callback这个Runnable        if (msg.callback != null) {            //执行callback方法,其实是执行run方法            handleCallback(msg);        } else {//如果创建Handler的时候创建了Callback对象            if (mCallback != null) {                //执行callback的handleMessage方法                if (mCallback.handleMessage(msg)) {                    return;                }            }            //让handler自己来处理msg            handleMessage(msg);        }    }  private static void handleCallback(Message message) {        //只是简单的调用了Runnable的run方法        message.callback.run();    } //简单的接口,提供了一个handleMessage方法来处理消息 public interface Callback {        //返回一个boolean值        public boolean handleMessage(Message msg);    }

所以Handler处理消息的过程其实也很简单:
a.检测Message的callback!=null,注意这个callback其实是一个Runnable,调用handleCallback方法其实就是执行这个Runnable的run方法而已。
b.如果创建Handler的时候,初始化了mCallack(这个callback并不是一个Ruannable,其实一个接口,该接口也很简单,就提供了一个handleMessage方法,由客户端决定怎么处理这条Message。
c.最后一步就是调用Handler的handleMessage方法来处理消息了。
通常我自己在使用Handler的时候就是用的定义一个Handler的子类,重写handleMessage方法来处理消息,倒是没有为Handler创建callback!
同时如果你设置了Handler的Callback,并且Callback的handleMessage方法如果返回true,那么Handler的handleMessage方法将不会执行;否则Handler的handleMessage方法也会得到执行!
所以通过上面的讲解,综合起来能得到下面的流程图:

到此位置android的消息机制可以说是解说完毕,不过还有些问题值得思考:我们说了这么多,那么android主线程(UI线程)的工作机制又是怎么样的呢?

UI线程的消息处理

在任何关于activity启动流程解析的资料中我们都会进入ActivityThread的main方法,这里当然不会再分析Activity的启动流程,拿来主义有时候还是不错的(需要注意一点ActivityThread不是一个Thread,只是一个普通的java对象:

 public static void main(String[] args) {        //创建主线程的Looper对象,prepareMainLooper内部实际上调用了        //Looper.prepare(false)方法,并把UI线程交给Looper的静态变量             //Looper sMainLooper;持有        Looper.prepareMainLooper();        //创建ActivityThread对象        ActivityThread thread = new ActivityThread();        thread.attach(false);        //创建一个Handler,这个Handler就是关联了UI消息队列的Handler        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        //开启了消息循环          Looper.loop();    }}

结合前面关于Looper的分析,其实ActivityThread的main方法也很简单,其流程如下:
1)调用Looper.prepareMainLooper()使得UI线程也就是主线程与一个Looper对象想关联。prepareMainLooper方法如下:

 //提供一个静态变量来持有UI线程的Looper对象 private static Looper sMainLooper;    public static void prepareMainLooper() {        //为UI线程创建一个Looper对象        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            //静态变量来引用这个关联了UI线程的Looper对象            sMainLooper = myLooper();        }    }

既然Looper用一个静态变量来保存关联了UI线程的Looper对象,那么我们可以调用Looper的如下两个方法检测是否是主线程:

//Picasso提供的检测是否是UI线程的方法 static boolean isMain() {    return Looper.getMainLooper().getThread() == Thread.currentThread();  }

而ImageLoader检测是否是UI线程的方法则是如下:

 Looper.myLooper() == Looper.getMainLooper()

2)创建一个Handler对象,根据前面的讲解这个Handler对象同样关联了UI线程的looper对象以及该looper对象的消息队列
3)调用Looper.loop()循环获取和处理消息。
需要注意的是:根据Looper源码的注释,android官方并不希望我们自己主动调用prepareMainLooper()方法。

简单的总结

这样UI线程的处理流程也简单的梳理完毕,那么还有一个问题:我们知道UI组件的更新是是在UI线程中进行的,也即是如果你在非线程中处理了某个任务后需要更新UI组件,那么在非UI线程线程工作完成后都需要交给UI线程来处理,怎么通知UI线程呢?你应该会知道答案:通过Handler,在UI线程中创建Handler,在非UI线程工作完毕后调用UI线程创建的Handler发送消息到UI消息队列,然后按照上面的流程图处理即可。
前面我既然分析了ImageLoader的源码(详见ImageLoader博客),那么就根据ImageLoader的工作原理把非UI线程和UI线程的工作流程也做个总结吧,如下图所示:

事实上,Activity内部就有一个mHandler对象,你应该知道Activity有个runOnUiThread方法,这个代码其实也没啥神秘的地方:判断当前线程是否是UI线程,如果不是就用mHandler调用post方法即可:

 public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

Message的复用机制

android开发老鸟都知道在使用Handler发送消息的时候不要使用Message msg = new Message()而改用Message msg = Message.obtain()方法,这样能达到Message对象的复用。那么message的复用机制是怎么实现的呢?在这里简单的说明一下。
先看看Message的对象是什么结构:

public final class Message implements Parcelable {      //指向下一个Message对象的引用      Message next;      private static final Object sPoolSync = new Object();    //可以复用的对象:static变量    private static Message sPool;    //可以复用Message的数量    private static int sPoolSize = 0;   //复用池的最大数值    private static final int MAX_POOL_SIZE = 50;} 

可以看出Message复用池中Message的最大数目是50条。且因为Message有一个next指针,所以sPool或者Message对象最终形成了一个链表的数据结构,如下图:

其中sPool指向的就是Message链表的首部。参照上图,让我们看看obtain方法都做了什么工作:

  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                //可复用的message个数-1                sPoolSize--;                return m;            }        }        return new Message();    }

其实obtain方法做的就是移除链表的首部 并将该首部返回的过程。执行完一次obtain后,sPool的结构就变成如下所示:

那么什么时候向sPool这个链表里添加元素呢?其实该操作在Message对象的recycleUnchecked方法里面:

//Message对象回收void recycleUnchecked() {       //数据清空操作        flags = FLAG_IN_USE;        what = 0;        arg1 = 0;        arg2 = 0;        obj = null;        replyTo = null;        sendingUid = -1;        when = 0;        target = null;        callback = null;        data = null;        //添加到了sPool链表的首部           synchronized (sPoolSync) {             //小于50个            if (sPoolSize < MAX_POOL_SIZE) {                next = sPool;                //sPool头部指针指向链表的首部                sPool = this;                sPoolSize++;            }        }    }

所以当我们对一个Message对象(假设是msg4)执行recycleUnchecked的时候,sPool变成了如下的结构:

那么什么时候调用recycleUnchecked方法呢?其实我们在使用Handler的时候在Activity或者Fragment的onDestroy方法中有这么一段:

  handler?.removeCallbacksAndMessages(null)

追踪下removeCallbacksAndMessages就知道了,该方法里调用了消息链表里每一个符合条件的Message对象的recycleUnchecked方法。
当然还有其他的地方也会执行recycleUnchecked方法,再次不在赘述。


HandlerThread简单说明

android提供了一个HandlerThread的工具类,这个类是Thread的一个子类HandlerThread extends Thread.这个线程的主要作用就是为这个thread创建了一个Looper对象,即HandlerThread是拥有了消息队列的一个线程,具体体现在该Thread的run方法:

   public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }

当该线程启动之后,消息队列就准备就位了。而且该类还提供了一个getThreadHandler()方法:

 //获取关联了当前对象Looper的    public Handler getThreadHandler() {        if (mHandler == null) {            mHandler = new Handler(getLooper());        }        return mHandler;    }

那么我们可以怎么使用这个对象呢?使用方式如下:
1、初始化一个HandlerThread对象

  HandlerThread handlerThread = new HandlerThread("IntentService[" + mName + "]");    handlerThread.start();

2、创建一个Handler对象:

  private final class ServiceHandler extends Handler {        public ServiceHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            onHandleIntent((Intent)msg.obj);            stopSelf(msg.arg1);        }    }

3、Hander和HandlerThread相关联:

   ServiceHandler    serviceHandler = new ServiceHandler(handlerThread.getLooper());

扯了这么多,HandlerThread有啥用呢?我们知道在android用有一个UI线程,UI线程也有一个Looper,我们在UI线程中发送的消息,最终会在UI线程中顺序的执行。那么HandlerThread其实也一样,我们通过serviceHandler发送的消息都被HandlerThread这个线程顺序的执行,因为其拥有自己的消息队列,所以不会阻塞UI的执行;当然虽然说是新线程,因为逐个执行的,所以对于耗时复杂的操作多个任务不建议使用HandlerThread,因为可能造成下一个消息等待时间过长。


到此为止android消息处理机制就简单的讲解完毕,如有不当的地方欢迎批评指正,共同学习!
其实多写博客还是有所帮助的,比如我在写这篇博客的时候,边分析Looper和Handler的源码,有的时候还需要ImageLoader的工作原理来配合加深理解和体会。如果我之前没有写过ImageLoader博客的话,理解或者体会或许就不那么深了,这也算是坚持写博客对自己的回报吧!

更多相关文章

  1. Android之Handling Runtime Changes(处理运行时更改)
  2. 微信消息中的电话超链接(ios vs Android)
  3. Android(安卓)系统复习面试系列(二)反射原理和 Android(安卓)类加
  4. react-native启动流程(android端)
  5. Android组件间的交互和进程间IPC通信
  6. Android多线程之线程池Executor详解
  7. Android异步任务AsyncTask的使用与原理分析
  8. Android(安卓)Wear 之 消息通知
  9. Android中TCP/IP 长连接 问题以及解决方案

随机推荐

  1. Android回调——Android模块化编程
  2. Android之SurfaceView学习
  3. android Progressbar使用
  4. Android MediaPlayer基本使用方式
  5. Android加载中动画
  6. android 中 浏览器调用本地app应用
  7. Android(安卓)Common Use
  8. Linear Layout
  9. 6
  10. Android系统源代码情景分析:基础知识