安卓 Handler 机制学习


一、一些基本概念

1. Handler机制的目的:

  • 耗时操作在独立线程操作
  • 跨线程传递消息
  • 消息托管
  • 消息处理的异步

2. handler与thread

  • Handler 作用是实现异步消息处理,Thread是新线程
  • Handler 的调用者仍为同一线程
  • 安卓的UI操作要求必须在主线程执行,如果Handler里操作过于耗时,调用者会阻塞。安卓提供了几种方式在其它线程来操作UI,如: Activity.runOnUiThread(Runnable), View的Post 、AsnycTask类等,它们基本都采用了Handler。
  • Handler直接调用线程的run方法。

3. handler的message处理

handler里使用Looper来处理Message,消息的处理是会阻塞的。

二、Handler机制组成部分

1. message

在线程间传递的对象稍为消息。Message.obtain()可以用来创建对象,这时创建对象类似是一个对象池,减少每次都new对象的开销。
Message是一个单向列表。

2. Handler

handler是message的处理器,同时也负责消息的发送和移除。

  • 发送即时消息: handler.sendMessage(Message msg)
  • 发送延时消息: handler.sendMessageDelayed(Message msg, long time)
  • 处理消息:handleMessage(Message msg)
  • 移除还未处理消息: handler.removeMessages(int what)

3. MessageQueue 消息队列

每个线程会有一个MessageQueue,message按when排序存放在消息队列 。

4. Looper 循环器

一个死循环,用来循环取出MessageQueue里的Message,交给Handler处理。每个线程只会有一个Looper对象。
Looper对象会持有1个MessageQueue对象、1个Thread对象(表示当前线程)、1个sMainLooper(指向主线程中创建的Looper对象)、1个ThreadLocal对象(负责持有作为线程局部变量的Looper对象)。

所以Thread-Looper-MessageQueue 之间的关系是一对一的。

三、 Handler的工作流程

  1. 创建线程、运行线程​​thread.start()​
  2. 创建Looper、MessageQueue​​Looper.prepare();​
  3. 启动Loop循环机制,监听MessageQueue​​Looper.loop();​
  4. 发送Message​​handler.sendMessage(msg); 或 handler.post(runnable);​
  5. Loop监听到新Message,取出处理。​​Looper.loop()​

四、Looper方法

1. 构造函数

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

构造Looper的时候会创建MessageQueue。

2. prepareMainLooper()

用来在主线程中创建Looper对象,一般APP框架调用这个方法,不能在其它线程调用。

定义

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// myLooper 用来返回当前线程中持有的Looper对象
sMainLooper = myLooper();
}
}

调用

public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
......
}
}

3. Looper.prepare()方法

静态方法用来创建MessageQueue,可以根据参数控制Looper是否可退出。这里使用ThreadLocal对象,这样不同线程可以保存不同的Looper。

public final class Looper{
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
public static void prepare(){
prepare(true);
}
public 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));
}
private Looper(boolean quitAllowed){
mQueue = ne MessageQueue(quitAllowed);
}
}

4. myLooer()方法

用来获取当前线程中持有的线程局部Looper对象变量。

public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

5. Looper.loop()方法

无限循环从MessageQueue中取出 Message对象。如果消息不为空就发送给Handler的dispatchMessage方法处理,如果为空会等待。

public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象,获取当前线程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}

final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列
....

for (;;) { //主线程开启无限循环模式
Message msg = queue.next(); //获取队列中的下一条消息,可能会线程阻塞
if (msg == null) { //没有消息,则退出循环,退出消息循环,那么你的程序也就可以退出了
return;
}
....
//分发Message,msg.target 是一个Handler对象,哪个Handler把这个Message发到队列里,
//这个Message会持有这个Handler的引用,并放到自己的target变量中,这样就可以回调我们重写
//的handler的handleMessage方法。
msg.target.dispatchMessage(msg);
....
....
msg.recycleUnchecked(); //将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。
}
}

五、MessageQueue

MessageQueue是一种消息队列的数据结果,其中重要的方法是 enqueueMessage()和 next() 方法。

  • 创建主线程的时候,会默认创建一个Looper,同时创建一个MessageQueue,
  • 创建子线程时,不会自动创建Looper,也不会创建MessageQueue,在调用Looper.prepare()的时候才会创建该线程的MessageQueue。

1. enqueueMessage

enQueueMessage主要操作:

  • 插入消息到消息队列
  • 唤醒Looper中等待的线程
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target就是发送此消息的Handler
throw new IllegalArgumentException("Message must have a target.");
}
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(TAG, e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;//将延迟时间封装到msg内部
Message p = mMessages;//消息队列的第一个元素
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),
//或者此消息不是延时的消息,则此消息需要被立即处理,
//此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
//然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,
//原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
//延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。
//插入延时消息不需要唤醒Looper线程。
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;
}

2. next()

  • next()里的​​nativePollOnce(ptr, nextPollTimeoutMillis);​​ 会阻塞消息,等待新消息到来。
  • 如果拿到消息还没时间,会听故事希望nextPollTimeoutMillis赋值,线程阻塞,等到时间自动唤醒
Message next() {

final long ptr = mPtr;
if (ptr == 0) {
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {

final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息
//设置mBlocked = false代表目前没有阻塞
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
//没有消息,会一直阻塞,直到被唤醒
nextPollTimeoutMillis = -1;
}

if (mQuitting) {
dispose();
return null;
}

//...
//此处有省略IdleHandler相关代码
//...

pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}

六、Handler

1. 构造函数

public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {

//.....此处省略n行代码......

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;
}

构造Handler的时候可以传入Callback,也可以不传。

2. 发送消息: sendMessage(msg)

sendMessage(msg)和post(Runnable)都会调用到sendMessageDelayed(Message msg, long delayMillis),最终调用 sendMessageAtTime方法。

3. 发送消息:post(Runnable)

这个时候消息会赋上callback属性值。

// 源码
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

使用方式:

mHandler.post(new Runnable() {
@Override
public void run() {
//主线程UI更新
}
});

4. sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 这里把mQueue赋值给本地的Queue,传给enQueueMessage,
// 而mQueue就是mLooper.mQueue
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);
}

4. dispatchMessage

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 使用post(runnable)的时候
handleCallback(msg);
} else {
// mCallback 是Handler的构造函数赋值
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

七、使用示例

1. 使用handler作延时处理

这里是向主线程添加延时消息。

binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "延迟处理", Toast.LENGTH_SHORT).show();
}
}, 1000*5);

}
});

2. 子线程更新UI

private void update(String text) {
TextView textView = findViewById(R.id.count);
textView.setText(text);
}

private final Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
Bundle bundle = msg.getData();
update(bundle.getString("text"));
}
}
};


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

com.cn.test.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

setSupportActionBar(binding.toolbar);

binding.fab.setOnClickListener(view -> {
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("text", (new Date()).getTime() + "");
message.what = 1;
message.setData(bundle);

handler.sendMessage(message);
});
}