Handler、Looper机制是Android运行的核心之一,也是面试的绝对热点
Handler、Looper机制由Looper、MessageQueue、Message、Handler组成
他们对象之间的持有关系如类图,为
- Looper对象中有MessageQueue对象
- Handler对象中有Looper对象和MessageQueue对象
- MessageQueue中有Message对象,该Message对象是一个链表的头结点
- Message对象中有Handler对象
注:本文采用先文字描述,后图片或代码展示的顺序
现在按该机制的时间顺序,分成一下4部分去分析
- 创建Looper
- 创建Handler
- 发送Message
- Looper从MessageQueue取出Message并执行指定的Handler的方法
1.创建Looper
Handler机制最先需要当前线程有一个Looper
如果当前线程尚未创建Looper,则会抛出异常,这个将在第2部分创建Handler时详细分析
我们通过静态方法Looper.prepare(),创建当前线程的Looper
Looper.prepare()调用了重载的带参数quitAllowed的私有静态方法
这里的传入的布尔类型有何用处,为何不直接创建Looper,先留个悬念
public static void prepare() {
prepare(true);
}
在static prepare(boolean quitAllowed)中,正式创建Looper
静态方法可以说是进程方法,静态变量只有一份,prepare()如何识别当前是哪个线程,并为线程创建Looper的呢
而且Thread并没有looper的对象,它们之间如何关联
这就用到了ThreadLocal
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));
}
ThreadLocal作用是,为每个线程存储一个泛型数据
在创建Looper时,先sThreadLocal.get()查看本线程是否有Looper
如果有就抛出"Only one Looper may be created per thread"异常,限制每个线程只能创建一个Looper
如果本线程没有Looper,则实例化一个Looper,添加到sThreadLocal去
static final ThreadLocal sThreadLocal = new ThreadLocal();
sThreadLocal.get();
sThreadLocal.set(new Looper(quitAllowed));
Looper在实例化时,会创建MessageQueue
Looper有且仅有一个MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
至此,为一个线程创建Looper的主要步骤结束
Android主线程的Looper当App启动的时候就会帮我们创建
在Framework的ActivityThread类的main()中,调用
Looper.prepareMainLooper();
Looper.prepareMainLooper()封装了static prepare(boolean quitAllowed)
发现,创建主线程Looper时quitAllowed为false,创建其他线程Looper时为true
可能有伙伴看quitAllowed字面意思就已经知道,主线程时传入quitAllowed,是为了Looper不能退出
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
quitAllowed变量最后作用与MessageQueue,在MessageQueue实例化时被赋值于mQuitAllowed
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
调用MessageQueue对象的quit方法,如果当前Looper是主线程Looper,抛出异常无法退出
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
......
}
2.创建Handler
创建Handler是为了执行我们的逻辑,加入我们的逻辑有三种方法
- Message对象的Runnable callback
- Handler的Callback mCallback
- 重写Handler的handleMessage(Message msg)
当它们同时存在时,只会执行一个,优先级即为我给的序号,这个在第4部分再来分析
Handler构造函数众多,但我们目前只需要关注Handler实例化的一个特性:
Handler会且仅会在构造函数中,获取当前线程的Looper对象
Handler对象中,只有构造函数的两处会对mLooper赋值
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
和
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
由上面代码可知
Handler(Callback callback, boolean async)构造时,如果当前线程没有创建Looper,mLooper为空,抛出异常
而Handler(Looper looper, Callback callback, boolean async)构造时,需要我们传入Looper
虽然这里没有抛出异常,当时官方注释有这么一句话 @param looper The looper, must not be null.
交给我们去保证Looper不为空
这里提出一个问题:任何Handler都能回到主线程和安全刷新UI吗?
答案:不是的
以上分析到Handler在构造时,便会去获取构造它的线程的Looper
如果实例化它的线程是其他线程,Handler中的逻辑,最后自然是在其他线程中执行了
并不能回到主线程和安全刷新UI
我们平时最经常这样使用Handler
在Activity、Fragment等类中,全局定义并实例化Handler的子类,能回到主线程
是因为作为全局变量,自定义的Handler子类在Activity、Fragment等被实例化时,跟着被实例化
实例化Activity、Fragment等在主线程中,这时候Handler中的Looper是主线程中的被实例化
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
}
为了更好地说明Handler会在哪个线程运行,本文最后附上了一个实验
有兴趣地伙伴可以最后去看一看
3.发送Message
Looper、Handler创建好了,此时不会执行任何Handler中的代码
需要我们用Handler发送消息到Looper的MessageQueue
执行哪个Handler的消息,就用那个Handler发送
handler.sendMessage(message);
此时进入Handler.Java源码
默认延迟参数为0,调用sendMessageDelayed
当然我们在需要消息延迟时,直接调用sendMessageDelayed
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
在sendMessageDelayed中,会获取当前的时间,加上延迟时间,作为此Message被执行的时间戳
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在sendMessageAtTime中,判断当前的MessageQueue是否为空
不为空时,调用enqueueMessage,再调用MessageQueue对象的enqueueMessage
将Message添加到Looper的MessageQueue中
此处还有一个关键步骤msg.target = this,这是将Handler对象记录在Message中
既实现了Message和Handler的绑定来执行指定的方法,
也通过Handler实现了Message和MessageQueue、Looper的关联
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
来到了关键的将Message插入MessageQueue步骤,MessageQueue采用链表结构
所以本质上就是链表的插入
boolean enqueueMessage(Message msg, long when) {
......
//线程安全
synchronized (this) {
......
msg.when = when;
Message p = mMessages;
......
if (p == null || when == 0 || when < p.when) {
// 如果MessageQueue为空,或当前Message优先级最高,将此Message作为头结点
msg.next = p;
mMessages = msg;
......
} else {
// 到达链表最后,或Message的执行时间比链表当前位置之后的靠前,插入Message
......
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
......
}
msg.next = p;
prev.next = msg;
}
......
}
return true;
}
4.Looper从MessageQueue取出Message并执行指定的Handler的方法
在Looper的静态方法Looper()中,主要做的是取出链表结构的Message,
再调用Handler对象的dispatchMessage(msg),执行指定逻辑
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
......
for (;;) {
Message msg = queue.next();
if (msg == null) {
// 如果Message链表的头结点为空,说明没有消息可执行,此次loop停止
return;
}
......
try {
// 执行消息承载的方法
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
在dispatchMessage中,将根据Message对象中的Runable对象、Handler对象中的Callback对象是否为空
从而实现了在第2部分中,Handler按优先级执行且执行一个的逻辑,这里再重复一下:
- Message对象的Runnable callback
- Handler的Callback mCallback
- 重写Handler的handleMessage(Message msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
至此, Looper从MessageQueue取出Message并执行指定的Handler的方法完成,实现了Handler机制的一次过程
实验
目的:验证Handler会在什么线程运行
内容:
①在其他线程中重写和实例化Handler,查看是否在主线程执行
②Hander在一个类中的全局重写和实例化,在其他线程实例化这个类,查看是否在主线程执行
③在Activity类中全局重写和实例化Handler,查看是否在主线程执行
代码:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ImageView mTestIV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTestIV = findViewById(R.id.main_iv);
//test01();
//test02();
test03();
}
//--------------------Test01--------------------
private void test01(){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
mTestIV.setImageResource(R.drawable.ic_launcher_background);
Log.d(TAG, "test01: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
}
}
};
Message message = new Message();
message.what = 0;
handler.sendMessage(message);
Looper.loop();
}
}).start();
}
//--------------------Test02--------------------
private void test02(){
new Thread(new Runnable() {
@Override
public void run() {
new Test02();
}
}).start();
}
class Test02{
public Test02() {
Looper.prepare();
Message message = new Message();
message.what = 0;
mHandler.sendMessage(message);
Looper.loop();
}
Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
mTestIV.setImageResource(R.drawable.ic_launcher_background);
Log.d(TAG, "test02: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
}
}
};
}
//--------------------Test03--------------------
private void test03(){
Message message = new Message();
message.what = 0;
mHandler.sendMessage(message);
}
Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
mTestIV.setImageResource(R.drawable.ic_launcher_background);
Log.d(TAG, "test03: handleMessage is MainThread: " + (Looper.myLooper() == Looper.getMainLooper()));
}
}
};
}
结果:
①不是主线程,UI操作成功
②闪退
③是主线程,UI操作成功
结论:
①在非UI线程更新UI仍然成功(但不安全,这个现象正常,我后面将以其他的文章,浅析为何)
②Looper是在Handler实例化时绑定上去的
③如果想要在一个类中写全局重写和实例化Handler,这个类的实例化要在主线程
最后
本码农学识尚浅,如果有理解不对的地方,非常希望大家指正
谢谢~