安卓 Handler 机制学习
- 1. 构造函数
- 2. 发送消息: sendMessage(msg)
- 3. 发送消息:post(Runnable)
- 4. sendMessageAtTime
- 4. dispatchMessage
- 七、使用示例
一、一些基本概念
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的工作流程
- 创建线程、运行线程
thread.start()
- 创建Looper、MessageQueue
Looper.prepare();
- 启动Loop循环机制,监听MessageQueue
Looper.loop();
- 发送Message
handler.sendMessage(msg); 或 handler.post(runnable);
- 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 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() {
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() {
public void onClick(View view) {
new Handler().postDelayed(new Runnable() {
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()) {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
Bundle bundle = msg.getData();
update(bundle.getString("text"));
}
}
};
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);
});
}