前言
最近刚好在做关于异步通信的需求,那么,今天我们来讲解下Android开发中的Handler异步通信传递机制(包括Looper、Message Queue)
目录
定义
Android提供的一套消息传递机制
作用
用于实现子线程对UI线程的更新,实现异步消息的处理:
- 在新启动的线程中发送消息
- 在主线程中获取并处理信息
为什么要用Handler
在安卓开发中:
- 为了保证Android的UI操作是线程安全的,Android规定了只允许UI线程修改Activity里的UI组件;
- 但在实际开发中,必然会用到多个线程并发操作UI组件,这又将导致UI操作的线程不安全
所以问题在于:如何同时满足:
Handler消息传递机制就是这个问题的。
相关概念
主线程(UI线程)
- 定义:当程序第一次启动时,Android会同时启动一条主线程(Main Thread)
- 作用:主线程主要负责处理与UI相关的事件,所以主线程又叫UI线程
子线程则负责一些比较耗时的操作(联网、取数据、SD卡数据加载等操作),而主线程和子线程之间的通信,就是要靠Handler了。
Message
- 定义:消息,理解为线程间通讯的数据单元(Handler接受和处理的消息对象。)
例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程
Message Queue
- 定义:消息队列
- 作用:用来存放通过Handler发过来的消息,按照先进先出执行
Handler
- 定义:Handler是Message的主要处理者
- 作用:负责将Message添加到消息队列&处理Looper分派过来的Message
Looper
Handler异步通信机制工作流程图
Handler、Looper、MessageQueue关系类图
Handler
- 提供sendMessage方法,将消息放置到队列中
- 提供handleMessage方法,定义个各种消息的处理方式;
Looper
- Looper.prepare():实例化Looper对象;为当前线程生成一个消息队列;
- Looper.loop() :循环从消息队列中获取消息,交给Handler处理;此时线程处于无限循环中,不停的从MessageQueue中获取Message 消息 ;如果没有消息就阻塞
MessageQueue
- 提供enqueueMessage 方法,将消息根据时间放置到队列中;
- 提供next方法,从队列中获取消息,没有消息的时候阻塞;
Handler工作流程解释
异步通信传递机制步骤主要包括异步通信的准备、消息发送、消息循环和消息处理
- 异步通信的准备
包括Looper对象的创建&实例化、MessageQueue队列的创建和Handler的实例化
- 消息发送
Handler将消息发送到消息队列中
- 消息循环
Looper执行Looper.loop()进入消息循环,在这个循环过程中,不断从该Message Queue取出消息,并将取出的消息派发给创建该消息的Handler
- 消息处理
调用该Handler的dispatchMessage(msg)方法,即回调handleMessage(msg)处理消息
好像很复杂?那就先看下这个简图了解下:
工作流程详细讲解
由上面可以得知,整个异步消息传递机制主要包括Handler、Looper和MessageQueue,接下来通过相应源码来解析这三部分
第一部分:Looper
Looper主要负责:
- 自身的创建&创建Message Queue
- 消息循环(消息取出、派发)
对应职责我们来看下相应的源码:
- 自身的创建&创建Message Queue:prepare()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static final void prepare() { //判断sThreadLocal是否为null,否则抛出异常 //即Looper.prepare()方法不能被调用两次 //也就是说,一个线程中只能对应一个Looper实例 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //sThreadLocal是一个ThreadLocal对象,用于在一个线程中存储变量 //实例化Looper对象并存放在ThreadLocal //这说明Looper是存放在Thread线程里的 sThreadLocal.set(new Looper(true)); } //再来看下Looper的构造方法 private Looper(boolean quitAllowed) { //创建了一个MessageQueue(消息队列) //这说明,当创建一个Looper实例时,会自动创建一个与之配对的MessageQueue(消息队列) mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
|
2. 消息循环:loop()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public static void loop() { //myLooper()方法作用是返回sThreadLocal存储的Looper实例,如果me为null,loop()则抛出异常 //也就是说loop方法的执行必须在prepare方法之后运行 //也就是说,消息循环必须要先在线程当中创建Looper实例 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //获取looper实例中的mQueue(消息队列) final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //进入消息循环 for (;;) { //next()方法用于取出消息队列里的消息 //如果取出的消息为空,则线程阻塞 Message msg = queue.next(); // might block if (msg == null) { return; } Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理 //Msg的target其实就是handler对象,下面会继续分析 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } 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的作用:
实例化本身、与当前线程绑定、创建与之相应的MessageQueue:prepare()方法
一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue
消息循环(消息取出、消息派发):loop()方法
不断从MessageQueue中去取消息,派发给消息的target属性的Handler,然后调用相应Handler的dispatchMessage()方法进行消息处理。
第二部分:Handler
主要负责:
- 在子线程发送消息给MessageQueue
- 处理Looper派发过来的消息
使用Handler之前,会初始化一个Handler实例
Handler是需要和线程绑定在一起的,在初始化Handler的时候一般通过指定Looper对象从而绑定相应线程,即给Handler指定Looper对象=绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。一般有两种方法创建:
- 通过Loop.myLooper()得到当前线程的Looper对象/通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
- 不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行.
首先看Handler的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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()); } } //通过Looper.myLooper()获取了当前线程保存的Looper实例,如果线程没有Looper实例那么会抛出异常 //这说明在一个没有创建Looper的线程中是无法创建一个Handler对象的 //所以说我们在子线程中创建一个Handler时首先需要创建Looper,并且开启消息循环才能够使用这个Handler。 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //获取了这个Looper实例中保存的MessageQueue(消息队列) //这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
|
- 上述说明:当Handler初始化时,可通过构造方法自动关联Looper和相应的MessageQueue
1. Handler向MessageQueue发送消息:对于Handler的发送方式可以分为post和send两种方式。
send的发送方法:sendMessage()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } //我们往下扒 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } 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 MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } //调用了enqueueMessage方法 return enqueueMessage(queue, msg, uptimeMillis); } //调用sendMessage方法其实最后是调用了enqueueMessage方法 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //为msg.target赋值为this,也就是把当前的handler作为msg的target属性 //如果大家还记得Looper的loop()方法会取出每个msg然后执行msg.target.dispatchMessage(msg)去处理消息,其实就是派发给相应的Handler msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //最终调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。 return queue.enqueueMessage(msg, uptimeMillis); }
|
Post的发送方法:sendMessage()
1 2 3 4 5 6 7 8
| showhandler.post(new Runnable() { @Override public void run() { String line = "\n"; StringBuffer text = new StringBuffer(show.getText()); text.append(line).append("angelababy:Yes,I do"); show.setText(text); }
|
- 相比send方法,post方法最大的不同在于,更新UI操作可直接在重写的run方法定义。
- 其实Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { //创建了一个Message对象 //创建Message对象可以new,也可以使用Message.obtain()方法; //但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。 Message m = Message.obtain(); //将我们创建的Runable对象作为callback属性,赋值给了此message. m.callback = r; //创建了一个Message对象 return m; } 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); }
|
从上面的源码发现了吧?和send中的handler.sendMessage是一样的。
调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终Handler将消息加入MessagQueue.
但是细心的你会发现,在使用Post方法时会将我们创建的Runable对象作为callback属性赋值给了此message
那么msg的callback和target都有值,那么会执行哪个呢?
我们已知回调发送消息的方法是:dispatchMessage()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public void dispatchMessage(Message msg) { //一开始就会进行判断 //如果msg.callback属性不为null,则执行callback回调,也就是我们的Runnable对象 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
|
####2. 处理Looper派发过来的消息:dispathMessage()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } public void handleMessage(Message msg) { }
|
- 可以看到dispathMessage()方法里调用了 handleMessage()方法,但handleMessage()是一个空方法
- 因为Handler发送消息过来是希望进行一定的处理,至于怎么处理消息是该Handler最终控制的,所以我们在创建handler时需要通过复写handleMessage()方法从而实现我们需要的消息处理方式,然后根据msg.what标识进行消息处理。
这就是为什么我们在主线程中实例化Handler的时候需要重写handleMessage()
特别注意
在一个Android应用启动的时候,会创建一个主线程,即ActivityThread(也叫UI线程),在ActivityThread中有一个静态的main方法:应用程序的入口点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| //一个进程会默认生成一个主线程 public static void main(String[] args) { ...... //主线程生成时自动通过prepareMainLooper方法为主线程创建一个Looper //prepare()方法是用于在子线程中创建一个Looper对象,在子线程中是可以退出消息循环的:调用消息队列的quit方法 //Looper生成时会自动生成与之配套的消息队列 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); ...... //loop()方法开启消息循环 //主线程的消息循环是不允许被退出的 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
|
第三部分:MessageQueue
即消息队列,用于存放Handler发送过来的消息
为了提高插入删除的效率,采用单链表的方式实现。
对于MessageQueue,我们来看下入队和出队操作
MessageQueue入队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| boolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { ...... 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; }
|
消息的入队(插入)过程
- 首先判断消息队列里有没有消息,没有的话则将当前插入的消息作为队头,并且这时消息队列如果处于等待状态的话则将其唤醒。
- 若是在中间插入,则根据Message创建的时间进行插入。
MessageQueue出队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| Message next() { ...... int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } //按照我们设置的时间取出消息 if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // 如果消息队列中没有消息,将nextPollTimeoutMillis设为-1,下次循环消息队列则处于等待状态 nextPollTimeoutMillis = -1; } //退出消息队列,返回null,这时候Looper中的消息循环也会终止。 if (mQuitting) { dispose(); return null; } ...... } ..... } }
|
Thread、Looper、Handler之间的对应关系:
- 一个Thread(线程)只能有一个Looper,可以有多个Handler
- 一个Looper可以绑定多个Handler;
- 一个Handler只能绑定一个Looper;
回顾工作原理图
把Handler工作原理都讲解完了,我们再来看下一开始说的工作原理图,你大概会有更深的理解了。
实例
没有实际应用的博客都不是好博客!现在是时候看一下实际应用了
Demo的源码下载
https://github.com/Carson-Ho/Handler_learning
(个人推荐先fork下来再对着下面的分析看,效果会更好哦!)
- 布局文件:
activity_main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.carson_ho.handlerdemo.MainActivity"> <TextView android:id="@+id/show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" /> </RelativeLayout>
|
2. 1 send方法:MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| package com.example.carson_ho.handlerdemo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView show; private Handler showhandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show = (TextView) findViewById(R.id.show); showhandler = new FHandler(); new Thread_1().start(); new Thread_2().start(); } class FHandler extends Handler{ @Override public void handleMessage(Message msg) { StringBuffer text = new StringBuffer(); switch (msg.what) { case 1: text.append("I love Carson_Ho"); show.setText(text); break; case 2: text.append("I hate Carson_Ho"); show.setText(text); break; } } } class Thread_1 extends Thread { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = Message.obtain(); msg.what = 1; msg.obj = "AA"; showhandler.sendMessage(msg); } } class Thread_2 extends Thread { @Override public void run() { try { Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = Message.obtain(); msg.what = 2; msg.obj = "BB"; showhandler.sendMessage(msg); } } }
|
2. 2 Post方法:MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package com.example.carson_ho.handlerdemo; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MainActivity extends AppCompatActivity { public TextView show; public Handler showhandler; @Override //主线程创建时便自动创建Looper和对应的MessageQueue,之前执行Loop()进入消息循环 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show = (TextView) findViewById(R.id.AA); StringBuffer text = new StringBuffer(); text.append("Carson_Ho:Do you love me?"); show.setText(text); //实例化Handler,这里并无指定Looper,即自动绑定当前线程(主线程)的Looper和MessageQueue showhandler = new Handler(); //启动子线程 new Thread_1().start(); new Thread_2().start(); } class Thread_1 extends Thread { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } showhandler.post(new Runnable() { @Override public void run() { String line = "\n"; StringBuffer text = new StringBuffer(show.getText()); text.append(line).append("angelababy:Yes,I do"); show.setText(text); } }); } } class Thread_2 extends Thread { @Override public void run() { try { Thread.sleep(8000); } catch (InterruptedException e) { e.printStackTrace(); } showhandler.post(new Runnable() { @Override public void run() { String line = "\n"; StringBuffer text = new StringBuffer(show.getText()); text.append(line).append("黄晓明:what the fuck?"); show.setText(text); } }); } } }
|
Demo的源码下载
https://github.com/Carson-Ho/Handler_learning
参考文献
http://blog.csdn.net/lmj623565791/article/details/38377229
总结
本文对Handler异步通信机制全面解析(包含Looper、Message Queue)进行了全面介绍和分析,接下来我会介绍继续介绍Android开发中的相关知识,有兴趣可以继续关注[Carson_Ho的安卓开发笔记]