Handler的实现原理及其与Message、MessageQueue关系详解
2015-08-13 15:39
941 查看
在Android中为我们提供了一个主线程和子线程之间的通信机制,这种机制就是利用Handler、Message、Looper和MessageQueue来彼此联系起来的。那么为什么要提供这么一种机制呢?因为Android是单线程模式的,所谓单线程模式就是说,在app启动时就会创建一个对应的唯一的一个主线程(ActivityThread实例),这个实例维护着界面ui的更新、用户的各种响应事件等。这些更新只能放在主线程中执行(典型的例子就是在Framework中会检查主线程中是否有网络请求的操作,如果有就会直接抛出异常),主线程并不安全,并且耗时的操作必须放在子线程中去执行,这里我不再讨论线程不安全、耗时操作不能放在主线程中执行的原因,前面的文章介绍过。基于这个原因,Framework引入了这么一种主线程和子线程通信的机制,Handler、Message、Looper和MessageQueue彼此协作完成通信。
4.在子线程中创建Handler对象。
记得我刚开始做Android开发入门在使用Handler时,总是牢记不能在主线程以外的地方创建使用Handler,然而,事实上我们可以在子线程中创建Handler,只需要在创建Handler的时候将主线程的Looper传递过去就行,如下所示:
当然我们也可以不用这么麻烦去自己使用Looper来创建Handler对象,可以直接使用HandlerThread来帮我们完成。 关于HandlerThrad的用法详见http://blog.csdn.net/u012481172/article/details/44243681;
第23条。
先调用Looper的getMainLooper()获取主线程的Looper,然后在创建Handler的时候再传递过去即可,这样Handler在sendMessage的时候就会将消息传递给主线程了。
以上我们介绍了Handler的几种简单用法,但是如果是新手的话肯定会不明所以,糊里糊涂的,所以下面就会介绍Handler的原理。
1、在线程中调用Looper.prepare()准备一个Looper。
2、在线程中调用Looper.loop()进入消息循环队列。
3、使用handler发送消息。
首先调用Looper的prepare()方法准备一个Looper对象将其放入ThreadLocal中:
首先创建一个MessageQueue对象mQueue,在进行消息循环时(就是执行looper()方法)会不断从mQueue中读取消息。然后又调用Thread.currentThread()获取当前程序所在的线程。通常如果我们直接在UI线程中创建Handler时,这个mThread就是一个主线程,如果是在子线程中创建Handler并创建消息队列时,此mThread就是当前的子线程。以下是Looper.prepare()的工作流图:
在Looper.prepare()完成后就可以调用loop()进入消息循环了,我们看下loop()的源码:
这里放一张我手绘的Looper.loop()的工作流图:
上图中用红线连接起来的三个msg对象表示的是相同的实例。
looper()方法的逻辑很简单,基本都在注释上面说明了,这里再梳理一遍:首先获取到Looper对象,然后取出该Looper对象的MessageQueue对象,并进入无限循环,不断的调用MessageQueue的next()方法读取Message,如果没有就直接退出,如果有就取出Message的Handler并执行该Handler的dispatchMessage()方法。那么我们再去看一下Handler的dispatchMessage()方法是什么鬼:
① 如果Message的callback(即Runnable)部位null那么Callback和Handler的handleMessage()都不会执行;
② 如果Message的callback是null且Callback不为nul,就去执行Callback的handleMessage(),如果Callback的handleMessage()执行成功就不会执行Handler的handleMessage(), 如果执行失败就会执行Handler的handleMessage();
③ 如果Callback也是null,那么就直接去执行Handler的handleMessage()方法。
所以显然,我们最常用的直接重写Handler的handleMessage()方法的优先级是最低的。
那么上面所说的Message的callback是在什么时候被赋值的呢?换句话说我们在什么情况下会使用到呢?答案就是在获取Message对象的时候用到,请看下面的例子:
我们最常用的是使用Handler的obtainMessage()来获取一个Message对象的,查看其源码可以知道,实际上Handler的obtainMessage()也是调用了Message的obtainMessage(this)方法的,其中this表示当前的Handler。只不过用此种方式获取Message不会有callback创建。
经过上面的分析我们知道了Looper是如何工作的,也知道了Handler的handlerMessage等方法是何时被调用的,也初步接触到MessageQueue,那么我们是如何将消息发送给MessageQueue的呢?接下来就将以下使用Handler发送消息的几种机制,我们从创建Handler说起。我们在使用Handler时一定会直接new一个Handler,它只提供一个public的构造器,因此只能通过new的方式创建,那么我们去看看他的构造器做了哪些事情:
使用Handler发送消息(Message)可以使用两个系列的方法实现,第一就是post(...)系列,第二就是sendMessage(...)系列。
1.post(...)系列:
(1)post(Runnable):似乎看起来没有使用到Message,但其实不然,因为在post(Runnable)方法中系统会帮我们创建一个Message对象,并且把Runnable对象赋值给该Message的callback,所以,如果使用了post(Runnable)方法,那么Handler的handleMessage()和Callback的handleMessage()都将无效。
(2)postAtTime(Runnable,miultTime):指定某时间点再去执行,原理同post(Runnable)一样。
....
2.sendMessage(...)系列:
(1)sendMessage(msg):就是单纯的发送一个Message。
....
....
不管是什么方法发送消息,最后都会调用到 sendMessageAtTime(Message msg, long uptimeMillis)方法:
首先Looper调用prepare()方法准备好一个MessageQueue,当然,一个app开启后主线程自己就创建了一个MessageQueue,在ActivityThread线程(就是主线程)中,Framework已经调用了Looper的prepare()并进入了loop()。当prepare()准备好了之后就调用loop()方法进入消息循环,当然第一次是没有消息的,因此会处于阻塞状态(在MessageQueue的next()方法处),如果有消息就读取一个Message,然后调用Message的dispatchMessage(),然后根据优先级以此调用Message的callback(一个Runnable对象)、Handler.Callback的handleMessage()、Handler的handleMessage()。另一方面,当我们要发送一个消息时,通常会调用sendMessage()或postMessage()等方法,本质上都是调用了sendMessageAtTime()方法,消息发送成功后会唤醒looper()继续执行。然后就调用到了dispatchMessage()了。
最后,关于Looper我们通常不会用到,除非你需要自己维护一个消息队列,而不使用主线程的消息队列,关于Message和Handler还有很多有用的方法可以使用,具体的自己一看便知了。最后放几张手绘的流程图!
第一部分 主线程与子线程之间的通信实例
本文分两部分介绍,第一部分先展示如何使用这种机制来完成主线程和子线程的通信,第二部分介绍其实现的原理源码分析。1、常规用法,在子线程中直接使用handler.sendMessage()方法
//创建Handler对象,并重写其处理方法 mUIhandler = new UIHandler() { @Override public void handleMessage(Message msg) { // 处理请求后的任务 } }; // 第一种方式 new Thread(new Runnable() { @Override public void run() { // 这里处理耗时、网络操作, // 下面处理完后发送消息给Handler来处理 Message msg = mUIhandler.obtainMessage(); msg.what = 0; // msg.obj = value; mUIhandler.sendMessage(msg); } }).start();上面的使用方法很简单,就是先创建一个Handler对象,然后重写其handleMessage()方法,接下来开启子线程,并执行run()中的请求业务,之后再获取Message对象,然后发送出去给Handler的handleMessage()处理。
2.指定时间点发送消息
long tm = SystemClock.uptimeMillis(); // 第二种方式,指定时间点开始执行 mUIhandler.postAtTime(new Runnable() { @Override public void run() { Message msg = mUIhandler.obtainMessage(); msg.what = 1; mUIhandler.sendMessage(msg); } }, tm + 15000);Handler提供了一个postAtTime()来处理业务,在使用时需要传递一个Runnable对象,在该对象的run()中执行相应的业务,同上,执行完后再发送消息给Handler的handleMessage()来处理,他的第二个参数是指定在某时间点来post到MessageQueue,这个参数需要根据SystemClock.uptimeMillis()方法来计算,这个方法返回的是系统开启启动的总时间,然后我们想要它在开机后的多长时间启动就加上那个时间就可以了。此外,Handler还提供了一个postDelayed()方法,此方法也是两个参数,第一个是Runnable对象,第二个是指定多少毫秒以后再执行,跟postAtTime()在使用上有点区别,但是本质上没有区别,因为postDelayed()的实现还是使用了SystemClock.uptimeMillis()+delayMillis来完成的。第二部分介绍原理时会说道。
3.使用Handler的Callback完成消息处理
final Handler callBackHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { String str = mContentTv.getText().toString(); mContentTv.setText(str + "\n\n第三种方式,使用Callback实现消息处理"); return false; } }); callBackHandler.post(new Runnable() { @Override public void run() { Message msg = callBackHandler.obtainMessage(); msg.what = 1; callBackHandler.sendMessage(msg); } });如上代码所示,我们在创建Handler对象时并没有直接重写其handleMessage()方法,而是在创建时传递了一个Callback对象,重写Callback的handleMessage()方法来完成消息处理。有一点不同的是Callback的handleMessage()方法有一个boolean返回值,而Handler的handleMessage()没有boolean返回值,所以前者更灵活一些,而且我们也可以使用Callback来灵活实现项目的“金字塔”分层的设计。因此,我们可以在不用继承Handler的基础上利用Handler.Callback来实现消息传递与处理,如果我们要实现一个Handler的子类,那就必须重写Handler的handleMessage()方法了。
4.在子线程中创建Handler对象。
记得我刚开始做Android开发入门在使用Handler时,总是牢记不能在主线程以外的地方创建使用Handler,然而,事实上我们可以在子线程中创建Handler,只需要在创建Handler的时候将主线程的Looper传递过去就行,如下所示:
new Thread(new Runnable() { @Override public void run() { //获取主线程的Looper Looper looper = Looper.getMainLooper(); Handler handler = new Handler(looper){ @Override public void handleMessage(Message msg){ //这里消息业务 } }; } }).start();
当然我们也可以不用这么麻烦去自己使用Looper来创建Handler对象,可以直接使用HandlerThread来帮我们完成。 关于HandlerThrad的用法详见http://blog.csdn.net/u012481172/article/details/44243681;
第23条。
先调用Looper的getMainLooper()获取主线程的Looper,然后在创建Handler的时候再传递过去即可,这样Handler在sendMessage的时候就会将消息传递给主线程了。
以上我们介绍了Handler的几种简单用法,但是如果是新手的话肯定会不明所以,糊里糊涂的,所以下面就会介绍Handler的原理。
第二部分 主线程和子线程的通信原理
要使用Android的Handler进行主/子线程通信需要以下步骤:1、在线程中调用Looper.prepare()准备一个Looper。
2、在线程中调用Looper.loop()进入消息循环队列。
3、使用handler发送消息。
new Thread(new Runnable(){ @Override public void run() { //第一步,准备Looper Looper.prepare(); Handler handler = new Handler(){ @Override public void handleMessage(Message msg){ //处理消息业务 } }; //第二步,进入消息循环 Looper.loop(); //发送消息 handler.sendEmptyMessage(1); } }).start();使用的步骤已经说明,现在说下其原理:
首先调用Looper的prepare()方法准备一个Looper对象将其放入ThreadLocal中:
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //new一个Looper对象,并放到ThreadLocal中 sThreadLocal.set(new Looper(quitAllowed)); }可以看到,在创建一个Looper对象后就将该对象放到ThreadLocal中去,在调用myLooper()时获取该Looper对象,我们再看看Looper的构造器做了哪些事情呢?
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
首先创建一个MessageQueue对象mQueue,在进行消息循环时(就是执行looper()方法)会不断从mQueue中读取消息。然后又调用Thread.currentThread()获取当前程序所在的线程。通常如果我们直接在UI线程中创建Handler时,这个mThread就是一个主线程,如果是在子线程中创建Handler并创建消息队列时,此mThread就是当前的子线程。以下是Looper.prepare()的工作流图:
在Looper.prepare()完成后就可以调用loop()进入消息循环了,我们看下loop()的源码:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { //上面说到,直接调用myLooper()获取到在prepare()中创建的Looper对象, //在myLooper()中只有一行代码:mThreadLocal.get()。 final Looper me = myLooper(); //如果没有Looper对象就抛出异常:prepare()没有在这个线程中调用。 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //获取到mQueue对象,这个对象就是在Looper中创建的,也即在prepare()或prepareMainLooper()中创建 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,这个地方有可能会出现阻塞情况,因为next()方法会有产生阻塞;如果为null就退出, 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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //Message的targer变量是一个Handler实例,当从MessageQueue中取出Message后就调用Message的一个 //Handler的dispatchMessage()方法 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); } msg.recycle(); } }
这里放一张我手绘的Looper.loop()的工作流图:
上图中用红线连接起来的三个msg对象表示的是相同的实例。
looper()方法的逻辑很简单,基本都在注释上面说明了,这里再梳理一遍:首先获取到Looper对象,然后取出该Looper对象的MessageQueue对象,并进入无限循环,不断的调用MessageQueue的next()方法读取Message,如果没有就直接退出,如果有就取出Message的Handler并执行该Handler的dispatchMessage()方法。那么我们再去看一下Handler的dispatchMessage()方法是什么鬼:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { //判断Message的callback是否为空,如果不为空就执行handleCallback(msg),Message的callback变量就是一个Runnable //如果该Runnable不为空就调用handleCallback(msg),里面就一行代码:msg.callback.start(); if (msg.callback != null) { handleCallback(msg); } else { //如果mCallback不为空就执行Callback的handleMessage。 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //执行Handler的handleMessage() handleMessage(msg); } }可以看到,在dispatchMessage()中最终调用了我们重写的handleMessage()方法。在dispatchMessage()方法中一系列的调用有一个优先级,即:首先如果Message的callback不为空就去执行,并且下面的Callback和Handler的handleMessage()都不会执行,如果Message的callback为空,就去判断Handler的Callback是否为空,不为空就去执行,如果执行成功就直接return掉,如果执行失败再去执行。总结一句就是:
① 如果Message的callback(即Runnable)部位null那么Callback和Handler的handleMessage()都不会执行;
② 如果Message的callback是null且Callback不为nul,就去执行Callback的handleMessage(),如果Callback的handleMessage()执行成功就不会执行Handler的handleMessage(), 如果执行失败就会执行Handler的handleMessage();
③ 如果Callback也是null,那么就直接去执行Handler的handleMessage()方法。
所以显然,我们最常用的直接重写Handler的handleMessage()方法的优先级是最低的。
那么上面所说的Message的callback是在什么时候被赋值的呢?换句话说我们在什么情况下会使用到呢?答案就是在获取Message对象的时候用到,请看下面的例子:
Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } }); //获取一个Message对象 Message msg = Message.obtain(handler, new Runnable(){ @Override public void run() { //执行耗时操作 } });如上所示使用Message的callback的场合,就是调用Message.obtain(handler,runnable)方法,该方法会将handler对象赋值给target变量,将runnable赋值给callback变量,target在Looper的loop()方法中会使用到,而callback会在Handler的dispatchMessage()中使用到。实际上当我们使用了上面的方式来获取一个Message时,最终的结果就是handler调用了它的dispatchMessage()然后导致了runnable的执行,这两个参数是有因果关系的,有了handler才会导致runnable执行。
我们最常用的是使用Handler的obtainMessage()来获取一个Message对象的,查看其源码可以知道,实际上Handler的obtainMessage()也是调用了Message的obtainMessage(this)方法的,其中this表示当前的Handler。只不过用此种方式获取Message不会有callback创建。
经过上面的分析我们知道了Looper是如何工作的,也知道了Handler的handlerMessage等方法是何时被调用的,也初步接触到MessageQueue,那么我们是如何将消息发送给MessageQueue的呢?接下来就将以下使用Handler发送消息的几种机制,我们从创建Handler说起。我们在使用Handler时一定会直接new一个Handler,它只提供一个public的构造器,因此只能通过new的方式创建,那么我们去看看他的构造器做了哪些事情:
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { 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()); } } //获取Looper,上面说过myLooper实际上就是把在prepare()中set到ThreadLocal中的Looper对象get出来。 mLooper = Looper.myLooper(); //如果mLooper为空,就说明我们没有调用prepare() if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //然后将Looper的mQueue赋值给Handler的mQueue,这个mQueue在发送消息时会用到。mLooper的mQueue也是在prepare() //的时候创建的 mQueue = mLooper.mQueue; mCallback = null; }创建完Handler后我们就需要获取Message对象了,关于获取Message对象,上面已经说过了,就不在赘述。接下来就是发送消息了。
使用Handler发送消息(Message)可以使用两个系列的方法实现,第一就是post(...)系列,第二就是sendMessage(...)系列。
1.post(...)系列:
(1)post(Runnable):似乎看起来没有使用到Message,但其实不然,因为在post(Runnable)方法中系统会帮我们创建一个Message对象,并且把Runnable对象赋值给该Message的callback,所以,如果使用了post(Runnable)方法,那么Handler的handleMessage()和Callback的handleMessage()都将无效。
(2)postAtTime(Runnable,miultTime):指定某时间点再去执行,原理同post(Runnable)一样。
....
2.sendMessage(...)系列:
(1)sendMessage(msg):就是单纯的发送一个Message。
....
....
不管是什么方法发送消息,最后都会调用到 sendMessageAtTime(Message msg, long uptimeMillis)方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }第一个参数就是一个Message对象,第二个参数就是延迟执行的时间,如果我们在使用时没有使用到这个参数就表示立刻去执行。可以看到,在该方法中,首先将当前的Handler对象赋值给Message的target变量,随后调用mQueue的enqueueMessage(msg,uptimeMillis)方法。需要注意这个mQueue变量是我们在创建Handler对象的时候从Looper中获取的,从根本上来讲的就是Looper调用prepare()的时候创建的。然后我们再去看看MessageQueue的enqueueMessage()方法做了哪些事情:
final boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } msg.when = when; Message p = mMessages; 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; } } if (needWake) { nativeWake(mPtr); } return true; }初步的调用流程就介绍到这里,在这里并没有更深层次的分析native层的使用,仅仅很肤浅的介绍了下其工作原理,最后总结一下其调用流程:
首先Looper调用prepare()方法准备好一个MessageQueue,当然,一个app开启后主线程自己就创建了一个MessageQueue,在ActivityThread线程(就是主线程)中,Framework已经调用了Looper的prepare()并进入了loop()。当prepare()准备好了之后就调用loop()方法进入消息循环,当然第一次是没有消息的,因此会处于阻塞状态(在MessageQueue的next()方法处),如果有消息就读取一个Message,然后调用Message的dispatchMessage(),然后根据优先级以此调用Message的callback(一个Runnable对象)、Handler.Callback的handleMessage()、Handler的handleMessage()。另一方面,当我们要发送一个消息时,通常会调用sendMessage()或postMessage()等方法,本质上都是调用了sendMessageAtTime()方法,消息发送成功后会唤醒looper()继续执行。然后就调用到了dispatchMessage()了。
最后,关于Looper我们通常不会用到,除非你需要自己维护一个消息队列,而不使用主线程的消息队列,关于Message和Handler还有很多有用的方法可以使用,具体的自己一看便知了。最后放几张手绘的流程图!
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories