Handler机制是Android的异步消息处理机制,用于在线程间传递消息,主要涉及到四部分:Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者;Message是消息主体;MessageQueue是消息队列,Handler发送的消息都会放入MessageQueue中等待处理;Looper是MessageQueue的管理者,里面有一个loop方法,无限循环的从MessageQueue中取出需要处理的消息(如果有),并交个Handler处理。
这里有几个概念,一个线程只能有一个Looper对象和MessageQueue对象,而一个线程可以创建多个Handler对象,每一个Handler发送的消息只能由自己进行处理。通常的使用时主线程创建Handler对象,子线程使用这个Handler对象发送消息,由于这个Handler绑定的是主线程的Looper,所以会进入主线程的MessageQueue,也会由主线程的Looper分发给这个Handler回调他的handleMessage方法对消息进行处理。其中的原理看完下面的源码分析就可以理解了。
Android的UI线程是应用的主线程,是非线程安全的。Android规定,所有关于UI的更新都必须在主线程中执行,如果尝试在子线程中执行UI的更新操作会导致程序崩溃。
public class MainActivity extends AppCompatActivity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();
}
}
上面尝试在主线程和子线程中都创建一个Handler的实例,运行程序后会发现程序崩溃,日志打印如下:
E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: com.xy.handlertest, PID: 3524
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:227)
at android.os.Handler.<init>(Handler.java:129)
at com.xy.handlertest.MainActivity$1.run(MainActivity.java:21)
at java.lang.Thread.run(Thread.java:923)
报错显示无法在子线程中创建handler,原因是因为该线程中没有调用Looper.prepare()方法。
接下来可以进行改造,在子线程中调用该方法。
public class MainActivity extends AppCompatActivity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2 = new Handler();
}
}).start();
}
}
发现程序正常运行。接下来探究一下为什么需要调用Looper.prepare()方法后,才能够创建Handler的实例。可以查看Handler类的无参构造方法:
@Deprecated
public Handler() {
this(null, false);
}
可以看到,这里调用的是有两个参数重载的构造函数。同时,这里标志了废除的注解,说明这个方法是不建议使用的,原因就像是上面的例子一样,如果该线程没有调用过Looper的prepare()方法,就会出现程序崩溃,所以这里不建议使用无参构造方法。至于Looper的prepare()方法中做了什么,接着往下看。
public Handler(@Nullable 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());
}
}
mLooper = Looper.myLooper(); //1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()"); //2
}
mQueue = mLooper.mQueue; //3
mCallback = callback; //4
mAsynchronous = async;
}
这个构造方法就是无参构造方法调用的,无参构造传入的参数是null和false。这个方法的注释1处,调用了Looper.myLoope()方法,并返回一个Looper对象。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread"); //1
}
sThreadLocal.set(new Looper(quitAllowed));
}
myLooper返回的是sThreadLocal.get(),而set在prepare()方法中。在注释1处,判断Looper的实例是否已经创建了,如果已经存在了就会抛异常,所以对于一个线程来说,只能调用一次prepare方法,也只存在一个Looper。在这个方法中会创建一个Looper的实例,并将其设置到sThreadLocal中,get的时候也是获取的这个Looper的实例。到这里就解释了最开始的实验中为什么调用了Looper.prepare()方法后,就可以在子线程中创建Handler的实例,而不调用就不能创建的原因了。
Handler对象的创建需要获取Looper的实例,而Looper的实例只有调用了Looper.prepare()方法才会创建。异常信息的打印就是在Handler的构造函数中的注释2处,由于没有获取到Looper对象而抛出的异常。
接着看回Handler的构造方法,注释3处获取Looper的MessageQueue,用于存储Handler发送的消息,这个后面分析。注释4处传入的是Handler执行的回调,这个后面也会有讲到。
现在的疑惑是为什么可以在主线程中直接创建Handler对象呢,这是因为应用启动的时候,主线程中会自动地调用Looper.prepare()方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
}
在ActivityThread的main方法中,会调用Looper.prepareMainLooper()方法, 在这个方法中就会调用prepare()方法。
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这个方法的最开始处就会调用prepare()方法,可以看到这个方法有一个废弃的注解,但是并不是意味着这个方法已经废弃了,而是不建议开发者自己去调用这个方法,给的解释是因为android环境自己会调用这个方法,不需要开发者去调用。所以主线程中可以创建Handler的实例,因为主线程初始化的时候已经为我们创建了Looper对象了。
在使用时,我们完整的消息构建和处理大致如下
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(Looper.getMainLooper()) { //1
@Override
public void handleMessage(@NonNull Message msg) {
Log.d("MainActivity", "handleMessage: " + msg.getData().getString("data"));
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(() -> { //2
Message msg = new Message(); //3
msg.what = 1;
Bundle bundle = new Bundle();
bundle.putString("data", "hello");
msg.setData(bundle);
handler.sendMessage(msg); //4
}).start();
}
}
注释1处创建一个匿名内部类,并重写了Handler这个父类的handleMessage方法,这个方法在Handler中是一个空实现,就是给其子类重写并书写消息处理逻辑的。注释2处创建子线程,并在注释3处创建Message对象,注释4处通过handler对象发送到主线程进行消息处理。
接下来看一下Message类的属性
Message可以携带多种消息,其中what是用户可以对此次Message定义的一个标签,用于标志特定的Message,并根据这个标签来处理Message。arg1和arg2是用于传输简单的消息,是setData的轻量级方案。obj可以存储对象信息。data就是通过setData()方法设置的Bundle类型的数据。target标志着这个消息是哪个Handler发送的。
Handler中有多个sendMessage相关的方法,除了标红的以外,其他的都会调用到sendMessageAtTime()方法。调用到sendMessageAtTime()是按照时间顺序存放到MessageQueue中,而sendMessageAtFrontOfQueue()方法这是将Message插入到MessageQueue队列的头部优先处理。
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);
}
mQueue这个MessageQueue的对象是在Handler的构造函数中从Looper中获取的,与Looper相关联。这里调用enqueueMessage()方法将Message放入到MessageQueue中。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里设置了target为当前的Handler对象,这是一个标志,标志着这个消息属于当前的Handler,后面Looper从MessageQueue中取出Message的时候就是根据这个target来分发给对应的Handler进行处理。最后调用MessageQueue的enqueueMessage将消息入消息队列。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) { //1
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse(); //2
msg.when = when; //3
Message p = mMessages; //4
boolean needWake;
if (p == null || when == 0 || when < p.when) { //5
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else { //6
// 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;
}
注释1处判断当前消息是否在使用中,如果在使用中则抛异常。注释2处设置当前消息为使用中。消息在消息队列中通常是按时间顺序来排列的,注释3这里就是将时间信息存储到Message的when中。注释4处获取消息队列的队列头部。注释5处判断,如果队列中没有消息或者当前消息没有延时(即when=0)或者当前的消息处理时间在表头消息的处理时间之前,则将当前这条消息插入到消息队列的头部。否则就在注释6处遍历消息队列,根据消息的处理时间when来将消息插入到队列的合适位置。
when这个参数的设置在Handler中
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里获取的是开机为止的时间的毫秒值。
至此就将Message消息插入到了MessageQueue的队列中了。实际上,MessageQueue中消息队列存储Message并不是使用的容器,而是只是保存有队列头的Message的对象,并且Message中有一个next属性指向了下一个的Message。就是依靠Message中的next属性来关联整个的消息队列。
至此就分析清楚了消息构建发送到消息入消息队列的整个流程。
Message分发到对应的Handler处理这个工作是由Looper来完成的。
在第二节中讲到的ActivityThread的main方法中不止调用了Looper.prepareMainLooper()这个方法,在最后还调用了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; //1
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false;
for (;;) { //2
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
注释1处设置当前处于loop循环中,在上面会判断这个标志,如果多次调用loop()方法会打印警告信息。注释2处死循环执行loopOnce方法,只有这个方法返回false时才会退出。
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); //1
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
loopOnce是执行一次取Message的操作,首先第一行获取Message,先是获取到MessageQueue对象再调用next()方法获取到一个Message对象。获取Message后在注释1处分发消息,可以看到注释1处是获取到了Message对象中的target之后调用target对象的dispatchMessage方法,那么这个target是什么呢?在上节中我们讲了,target存储的就是发送Message的Handler对象。
首先看一下MessageQueue的next()方法,看一下是怎么获取到Message的
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); //1
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; //2
if (msg != null && msg.target == null) { //3
// 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) { //4
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
}
注释1处是使Looper阻塞的地方,是一个native方法。当没有消息处理的时候,就会在这里阻塞,释放cpu资源。注释2处获取消息队列的头节点,注释3处判断如果消息中的target不存在则找下一个消息,直到获取到Message后退出,target等于null相当于没有Handler,则不知道讲消息交给谁处理。注释4处如果消息不为空,则在这个里面执行消息的取出和返回操作。这里首先判断消息是否到期需要处理了,如果没有到期则不做处理,执行下一次loop循环,如果到期了则从队列中移除这个Message并返回。
(待补充:这里对于阻塞和唤醒没有分析,后面如果懂了这一块再来补充,猜测应该是nativePollOnce这个方法中进行判断的,在分发操作时,首先判断第一个Message的when是多久,分两种情况,一种是第一个都没有到期的,由于我们知道消息在队列中是按照到期时间来排列的,第一个都没有到期,后面的也没有,所以对于这种情况,算出到期时间,然后下一次loopOnce的时候会阻塞在nativePollOnce这个方法中,释放cpu资源。第二种情况就是到期了,则直接将有效的Message进行返回并移除出队列。next方法中后面还有nextPollTimeoutMillis = -1这一行,应该是标志没有消息,等待有消息才会重新唤醒Looper)。
然后看一下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);
}
}
这里判断Message的callback属性有没有值,如果有则调用handleCallback方法,如果没有则判断Handler的mCallback属性有没有值,有则调用这个里面的handleMessage方法,没有则调用Handler的handleMessage方法。这里其实涉及到了其他几种使用Handler的方式。请参考(待补充)。这里前面我们使用的方式是创建匿名内部类,重写Handler的handleMessage方法来创建的Handler,所以这里会调用最后面的Handler中的handleMessage方法。