一. Handler其实有两大主要作用
线程间通信
可以指定messgage和runnable在未来的某个时间节点执行,也就是按照一定的时间顺序执行
二.Handler的使用 Handler的简单使用 使用handler发送消息,需要两步,首先是创建一个Handler对象,并重写handleMessage方法,然后需要消息通信的地方,通过Handler的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 public class MainActivity extends Activity { private Handler mHandler; private static final int MSG_SUB_TO_MAIN= 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1.创建Handler,并重写handleMessage方法 mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 处理消息 switch (msg.what) { case MSG_SUB_TO_MAIN: // 打印出处理消息的线程名和Message.obj Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj); break; default: break; } } }; // 创建一个子线程,在子线程中发送消息 new Thread(new Runnable() { @Override public void run() { Message msg = Message.obtain(); msg.what = MSG_SUB_TO_MAIN; msg.obj = "这是一个来自子线程的消息"; // 2.发送消息 mHandler.sendMessage(msg); } }).start(); } }
这样就完成子线程向主线程发送消息。 如果想要主线程向子线程发送消息是否也只要在子线程中创建Handler对象,然后在主线程中拿到子线程的Handler以后,调用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 public class MainActivity extends Activity { private static final String TAG = "MainActivity"; private Handler mHandler; private static final int MSG_SUB_TO_MAIN= 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1.创建Handler,并重写handleMessage方法 // 创建一个子线程,并在子线程中创建一个Handler,且重写handleMessage new Thread(new Runnable() { @Override public void run() { subHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 处理消息 switch (msg.what) { case MSG_MAIN_TO_SUB: Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj); break; default: break; } } }; } }).start(); findViewById(R.id.btn) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message msg = Message.obtain(); msg.what = MSG_MAIN_TO_SUB; msg.obj = "这是一个来自主线程的消息"; // 主线程中发送消息 mHandler.sendMessage(msg); } }); } }
原因说的是在使用Handler之前没有调用Looper.prepare()。既然如此我们给它加上Looper.prepare()和Looper.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 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { ... // 1.创建Handler,并重写handleMessage方法 new Thread(new Runnable() { @Override public void run() { Looper.prepare(); subHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 处理消息 switch (msg.what) { case MSG_MAIN_TO_SUB: Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj); break; default: break; } } }; Looper.loop(); } }).start(); ... } }
1. 创建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 public class Handler { ... 省略部分代码 public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { // 不相关代码 ...... //得到当前线程的Looper,其实就是调用的sThreadLocal.get mLooper = Looper.myLooper(); // 如果当前线程没有Looper就报运行时异常 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 把得到的Looper的MessagQueue让Handler持有 mQueue = mLooper.mQueue; // 初始化Handler的Callback,其实就是最开始图中的回调方法的2 mCallback = callback; mAsynchronous = async; } ... 省略部分代码 }
从上面代码中我们了解到在一个线程中创建handle时会调用Looper.myLooper()方法:
1 2 3 4 5 6 7 8 public class Looper { ... 省略部分代码 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ... 省略部分代码 }
这里我们看到有一个ThreadLocal类。这个类的作用是干啥的呢?ThreadLocal是存放某个线程上下文的变量。
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 public class ThreadLocalTest { static ThreadLocal<String> mThreadLocal = new ThreadLocal<>(); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 mThreadLocal.set("local1"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + mThreadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程2中本地变量的值 mThreadLocal.set("local2"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + mThreadLocal.get()); } }); t1.start(); t2.start(); } static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + mThreadLocal.get()); //清除本地内存中的本地变量 mThreadLocal.remove(); } }
输出结果为:
1 2 3 4 thread1 :local1 thread2 :local2 after remove : null after remove : null
ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。也就是说往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。
既然Looper.myLooper()方法调用了sThreadLocal.get()
获得了空的Looper对象,说明我们并没有调用sThreadLocal.set()
方法给ThreadLocal赋值,找到sThreadLocal的set赋值函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Looper { ... 省略部分代码 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"); } sThreadLocal.set(new Looper(quitAllowed)); } ... 省略部分代码 }
我们看到要调用Looper的prepare方法就是可以给sThreadLocal赋值。 好了,到这里我们就知道怎么解决刚才的子线程报错的问题了。就是我们在初始化Handler时没有调用Looper的prepare方法给线程初始化Looper对象导致的,因此做出如下修改:
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 class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { ... // 1.创建Handler,并重写handleMessage方法 new Thread(new Runnable() { @Override public void run() { Looper.prepare(); subHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 处理消息 switch (msg.what) { case MSG_MAIN_TO_SUB: Log.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj); break; default: break; } } }; } }).start(); ... } }
通过ThreadLocal.get获取到当前线程的looper(所以如果一个线程没有初始化过looper就报运行时异常), 通过Looper对象获取到MessagQueue对象让Handler持有,设置处理回调的Callback对象。
Handler的创建过程主要有以下几点:
创建Handler对象。
得到当前线程的Looper对象,并判断是否为空。
让创建的Handler对象持有Looper、MessageQueu、Callback的引用。
使用Handler通信之前需要有以下四步:
调用Looper.prepare()
创建Handler对象
调用Looper.loop()
handler发送消息
1. 创建Handler对象 在一个线程中创建handle时会通过Looper.myLooper()获取到当前线程的looper
(所以如果一个线程没有初始化过looper就报运行时异常)。Looper对象会持有MessagQueue对象, 通过Looper可以获取到MessagQueue对象让Handler持有。并且设置处理回调的Callback对象。
handler初始化部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Handler { ··· public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { // 不相关代码 ...... //得到当前线程的Looper,其实就是调用的sThreadLocal.get mLooper = Looper.myLooper(); // 如果当前线程没有Looper就报运行时异常 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 把得到的Looper的MessagQueue让Handler持有 mQueue = mLooper.mQueue; // 初始化Handler的Callback mCallback = callback; mAsynchronous = async; } ··· }
Handler的创建过程主要有以下几点:
创建Handler对象。
得到当前线程的Looper对象,并判断是否为空。
让创建的Handler对象持有Looper、MessageQueu、Callback对象的引用。
2. Looper.prepare() 一个线程最多只有一个Looper对象。在线程里通过Looper.prapre()生成looper对象,当没有Looper对象时,去创建一个Looper,并存放到ThreadLocal中。 ThreadLocal存储了Looper对象的副本,并且可以通过它取得当前线程在之前存储的Looper的副本。looper对象的构造函数中会创建MessageQueue()对象。 Looper将持有MessageQueue对象。 Looper.prepare()源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Looper { public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { // 规定了一个线程只有一个Looper,也就是一个线程只能调用一次Looper.prepare() if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } // 如果当前线程没有Looper,那么就创建一个,存到sThreadLocal中 sThreadLocal.set(new Looper(quitAllowed)); } }
Looper的构造方法源码:
1 2 3 4 5 6 7 8 public class Looper { private Looper(boolean quitAllowed) { // 创建了MessageQueue,并供Looper持有 mQueue = new MessageQueue(quitAllowed); // 让Looper持有当前线程对象 mThread = Thread.currentThread(); } }
Looper.prepare()的作用主要有以下三点:
为当前线程创建Looper对象。
创建MessageQueue对象,并让Looper对象持有。
让Looper对象持有当前线程。
为何在android主线程中没有调用Looper.prepare()也可以在在主线程中使用handler? 原因是android主线程在ActivityThread.main方法中调用了Looper.prepare,所以在android主线程中无需再调用Looper.prapre()方法。 ActivityThread.main部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void main(String[] args) { // 不相干代码 ...... // 1.调用Looper.prepareMainLooper,其实也就是调用的Looper.loop,初始化Looper、MessageQueue等 Looper.prepareMainLooper(); // 2.创建ActivityThread的同时,初始化了成员变量Handler mH ActivityThread thread = new ActivityThread(); thread.attach(false); // if (sMainThreadHandler == null) { // 把创建的Handler mH赋值给sMainThreadHandler sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // 3.调用Looper.loop()方法,开启死循环,从MessageQueue中不断取出Message来处理 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
3. Looper.loop() Looper.loop()也会检查当前线程是否有Looper,然后通过Looper对象得到当前线程的MessageQueue。Looper.loop()会开启一个死循环,不断的从Looper对象的MessageQueue的next方法取出MessageQueue中的Message,然后使用message的target(实际上就是handler)进行分发。当MessageQueue中没有消息时,next方法会阻塞,导致当前线程挂起。
Looper.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 public static void loop() { // 得到当前线程的Looper对象 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // 得到当前线程的MessageQueue对象 final MessageQueue queue = me.mQueue; // 无关代码 ...... // 死循环 for (;;) { // 不断从当前线程的MessageQueue中取出Message,当MessageQueue没有元素时,方法阻塞 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // Message.target是Handler,其实就是发送消息的Handler,这里就是调用它的dispatchMessage方法 msg.target.dispatchMessage(msg); // 回收Message msg.recycleUnchecked(); } }
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 29 30 31 32 33 34 35 36 37 38 39 40 /* 这里我们看到,mLooper()方法里我们取出了,当前线程的looper对象,然后从looper对象开启了一个死循环 不断地从looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用 dispatchMessage去分发消息,通过代码可以看出target就是我们创建的handler。我们在继续往下分析Message的分发 */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } /*好了,到这里已经能看清晰了 可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法 */ private static void handleCallback(Message message) { message.callback.run(); } //即,如果我们在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。比如我们经常写的runOnUiThread方法: runOnUiThread(new Runnable() { @Override public void run() { } }); public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } } /* 而如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的, 所以handler的handlerMessage方法的执行也会在主线程中。
4. handler发送消息 Handler的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 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其实就是创建handler时的MessageQueue,默认情况是当前线程的Looper对象的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,把消息加入到MessageQueue中 return enqueueMessage(queue, msg, uptimeMillis); }
首先看看handler的enqueueMessage的实现,看看该方法:
1 2 3 4 5 6 7 8 9 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 把当前Handler对象,也就是发起消息的handler作为Message的target属性 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } // 调用MessageQueue中的enqueueMessage方法 return queue.enqueueMessage(msg, uptimeMillis); }
这里把当前Handler设为Message的target属性,方便Looper从MessageQueue中取出Message时进行消息分发的处理。然后调用了MessageQueue的enqueueMessage方法,把handler发送的消息加入到MessageQueue,供Looper去取出来处理。 MessageQueue的enqueueMessage方法:
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 boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } // 一个Message,只能发送一次 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("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } // 标记Message已经使用了 msg.markInUse(); msg.when = when; // 得到当前消息队列的头部 Message p = mMessages; boolean needWake; // 当前队列没有其他需要发送的Message。 //或者当前新添加进来的的Message的时间点为0(即需要立即发送的消息)。 //或者当前新添加进来的的Message需要发送的时间点小与当前MessageQueue队列头部Message的时间点(即当前添加进来的Message需要在当前MessageQueue队列头部Message之前被发送)时就会将当前新添加的Message插入到了MessageQueue的队首。 //我们这里when为0,表示立即处理的消息 if (p == null || when == 0 || when < p.when) { // 把消息插入到消息队列的头部 msg.next = p; mMessages = msg; needWake = mBlocked; } else { // 根据需要把消息插入到消息队列的合适位置,通常是调用xxxDelay方法,延时发送消息 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; }
首先,判断了Message是否已经使用过了,如果使用过,则直接抛出异常,这是可以理解的,如果MessageQueue中已经存在一个Message,但是还没有得到处理,这时候如果再发送一次该Message,可能会导致处理前一个Message时,出现问题。
然后,会判断when,它是表示延迟的时间,我们这里没有延时,所以为0,满足if条件。把消息插入到消息队列的头部。如果when不为0,则需要把消息加入到消息队列的合适位置。
最后会去判断当前线程是否已经阻塞了,如果阻塞了,则需要调用本地方法去唤醒它。
handler发送消息的本质都是:把Message加入到Handler中的MessageQueue中去。
主线程中的Looper.loop()为什么不会造成ANR? 显而易见的,如果main方法中没有looper进行循环,那么主线程一运行完毕就会退出。这还玩个蛋啊!
总结:ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的应用也就退出了。
我们知道了消息循环的必要性,那为什么这个死循环不会造成ANR异常呢?
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施。
如果某个消息处理时间过长,比如你在onCreate(),onResume()里面处理耗时操作,那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR。
让我们再看一遍造成ANR的原因,你可能就懂了。
造成ANR的原因一般有两种:
当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
当前的事件正在处理,但没有及时完成
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从 管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
总结:Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
在Handler机制中,每一个线程有一个Looper,Looper.loop()一直无限循环,为什么没有造成ANR呢?
首先得知道造成ANR的根本原因是:
1.在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)
2.BroadcastReceiver在10秒内没有执行完毕
造成以上两点的原因有很多,比如在主线程中做了非常耗时的操作,比如说是下载,io异常等。
再看Android主线程:
Java程序我们都知道,入口从main()方法执行,安卓用java,也不例外,入口从ActivityThread的main();
在main()里执行Looper.loop(),如果Looper不死循环,主线程执行完,就结束了,那APP还有存在意义吗?
造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。
主线程每隔16ms发送一个消息,用于刷新重绘UI,如果这个消息没有及时的消费,那么页面就会有卡顿感,严重的就是Application Not Resposing。
参考资料:图解Handler机制 Handler课后题 Android Handler消息机制原理最全解读(持续补充中) Handler进阶之sendMessage原理探索 解析Android中Handler机制原理 Handler相关面试题你答对多少?怎样清晰表达拿下面试官? Android Handler机制 Android Handler机制彻底梳理 一文读懂Handler机制