安卓 Handler 机制学习
Posted 编程圈子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓 Handler 机制学习相关的知识,希望对你有一定的参考价值。
安卓 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的工作流程
- 创建线程、运行线程
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 @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() + "")安卓 Handler 机制学习
Android :安卓学习笔记之 Handler机制 的简单理解和使用
深入源码分析Handler 消息机制 LooperMessageQueue 消息同步屏障IdleHandlerMessage 复用