Handler机制
线程间通信
Handler在android中应用非常普遍,主要用于线程间通信,比如在子线程中做耗时任务,然后通过handler机制在主线程刷新UI。
不同线程中Handler的使用
1.主线程中使用Handler
public class TestActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
text = findViewById(R.id.text);
Handler handler1 = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
text.setText("TextView");
}
};
Message msg = Message.obtain();
handler1.sendMessage(msg);
}
}
可以看到,主线程中只需要实例化Handler对象,然后通过sendMessage()方法将message发送出去,就可以在handleMessage()方法中接受到消息并进行UI操作
2.子线程中使用Handler
public class TestActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
new Thread(){
Handler handler2;
@Override
public void run() {
handler2 = new Handler();
Message msg = Message.obtain();
handler2.sendMessage(msg);
}
}.start();
}
}
如上,我们在子线程中也实例化一个Handler对象,然后直接通过sendMessage()发送消息,看会有什么结果
可以看到,在子线程中不能直接使用Handler,需要先调用Looper.prepare(),修改一下看看
public class TestActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
new Thread(){
Handler handler2;
@Override
public void run() {
Looper.prepare();
handler2 = new Handler();
Message msg = Message.obtain();
handler2.sendMessage(msg);
}
}.start();
}
}
添加*Looper.prepare()*之后,运行没问题。为什么之前在子线程中使用Handler需要调用Looper.prepare()呢?
要搞清楚这个问题,我们可以从问题点切入,首先找到相关报错信息的根源,因为在子线程中,我们只是实例化了一个Handler对象,所以我们取Handler源码里面去搜寻相关报错信息,通过搜索,我们可以找到报错信息的定位
可以看到,这个异常信息是在Handler的构造函数中抛出的,如果当前Handler中的looper为null,则抛出异常。而looper又是通过Looper.myLooper()获取的,因此,我们需要看下Looper里面的具体实现。
我们首先来看Looper.myLooper()源码
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
从ThreadLocal中获取Looper然后直接返回,而Looper实例又是通过Looper.prepare()存入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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
所以这里就解释了为何在子线程中使用Handler需要先调用Looper.prepare()。
那为何在主线程中使用Handler我们又没有先调用Looper.prepare()呢?我们知道,要是用Handler,必须先通过Looper.prepare()创建一个Looper对象并将其存入ThreadLocal中,因此主线程中必定在某个地方事先创建过Looper对象。提到主线程,我们立马就会想到ActivityThread.main(),我们打开其源码看看
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
// ...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这里我们省略了无关代码,可以看到,在main()中,事先调用了Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepareMainLooper()又调用了prepare()创建了Looper对象。这就解释了为何在主线程中我们不必手动调用Looper.prepare()来创建Looper了。
Handler是如何实现线程间通信的
我们平常使用Handler都是在主线程中创建Handler对象,然后在子线程中做耗时任务,得到结果后通过Handler将消息发送到主线程做UI操作,如
public class TestActivity extends AppCompatActivity {
private TextView text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
text = findViewById(R.id.text);
Handler handler1 = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
text.setText("TextView");
}
};
MyThread thread = new MyThread(handler1);
new Thread(thread).start();
}
static class MyThread implements Runnable{
Handler myHandler;
MyThread(Handler handler) {
myHandler = handler;
}
@Override
public void run() {
// 1.做耗时任务
// 2.将耗时任务得到的记过通过handler发送给主线程进行UI操作
Message msg = Message.obtain();
myHandler.sendMessage(msg);
}
}
}
我们在线程MyThread中做耗时任务,然后通过主线程中创建的Handler将消息发送出去,最后在Handler.handleMessage()中收到消息做UI操作。这里就涉及到子线程与主线程的通信。
发送消息时,我们通常是使用Handler.sendMessage()方法,那我们从这个方法入手,来看看Handler发送消息到接受消息的整个过程是怎样实现的。
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 将Handler自身引用赋值给Message的target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,sendMessage()最后会调用其enqueMessage()方法,这里我们要注意一点:Handler将自身引用赋值给了Message的target。 而最终会调用MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
// ... 省略无关代码
Message p = mMessages;
// 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;
}
return true;
}
在enqueueMessage()里,通过死循环不断地遍历Message的下一个节点,直到为null跳出循环,然后将Message.next赋值为最后的一个Message,到这里,Handler将消息加入到消息队列的过程就结束了。
既然Handler负责将Message发送到了MessageQueue中,那势必会有一个专门负责将消息从消息队列中取出的角色,这时就轮到Looper出场了。
Looper
Looper通过loop()方法将消息从MessageQueue中不断地取出消息
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// ...省略无关代码
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// ...省略无关代码
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
return true;
}
这个方法通过在一个死循环中遍历MessageQueue中的每一个Message(Message msg = me.mQueue.next()),得到的msg有调用了msg.target.dispatchMessage(msg),这个msg.target是什么呢? 前面我们其实已经说了Handler将自身引用赋值给了Message的target,因此这里的msg.target其实就是Handler,紧接着我们来看看Handler.dispatchMessage()方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看到这里,原来最终是调用的其自身的handleMessage()方法,而子线程中创建的Message最终也发送到了Handler所在的主线程中,实现了跨线程通信
Looper.loop()死循环为什么不会阻塞主线程,造成UI卡顿呢?
Looper.loop()通过死循环的方式不断地通过MessageQueue.next()方法来取出消息队列中的消息,next()方法调用了native方法nativePollOnce(),其底层实现是借助了Linux的epoll机制,简单来说就是当消息队列中没有消息时会进入阻塞状态,此时主线程会进入休眠状态并释放CPU资源,直到有新的消息进入消息队列后重新唤起主线程。当我们的应用在运行时,我们的主线程是一直存在的,因为Looper.loop()是一个死循环。主线程需要接收各种系统或用户户消息进而做出相应的处理。比如Activity的生命周期onCreate()之所以可以调用到,就是因为主线程中的Handler(ActivityThread中有一个Handler的引用H)接收到了系统发送过来的onCreate()事件进而调用了Activity的onCreate()方法。因此我们应用的所有事件都会通过Handler被动地触发,只要这些事件中不会出现太耗时的操作就不会出现卡顿现象。
总结
- Handler负责将消息Message发送到消息队列MessageQueue中
- MessageQueue通过enqueueMessage()方法将消息Message加入消息队列中
- Looper负责将消息Message从消息队列MessageQueue中取出
- Looper通过loop()方法不断地遍历MessageQueue的下一个节点消息,得到消息Message后通过持有的Handler引用Message.tarter调用dispatchMessage()方法将消息发送出去,最后在Handler.handleMessage()方法中接收消息
- Handler主要用于线程间通信:子线程中通过Handler发送消息,主线程中通过Handler.handleMessage()方法接收消息
- 主线程中Looper.loop()不会造成应用卡顿,因为所有的事件都会被动地调用到。
参考链接
Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
Android异步消息处理机制完全解析,带你从源码的角度彻底理解