Handler机制相关概念
- Handler
发送和处理消息。 - Looper
循环从消息队列取数据。 - Runnable和Message
任务和消息,任务最终也会转化成message。 - MessageQueue
消息队列,存储消息数据。
Handler机制流程及源码分析
Android开发中案例中,经常在子线程产生的数据发送到主线线程进行处理。处理流程类似于生产者-消费者,子线程(生产者)和消费者(主线程)同个时间段公用存储空间(Message)。
private static Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//处理消息
return false;
}
});
//发送消息
mHandler.sendEmptyMessage(0);
mHandler.post(new Runnable() {
@Override
public void run() {
}
});
流程及源码分析
创建Looper
主线程创建,在主线程即UI线程ActivityThread启动时(main函数)就通过Looper.prepareMainLooper()创建了。
//frameworks\base\core\java\android\app\ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
//frameworks\base\core\java\android\os\Looper.java
public static void prepareMainLooper() {
prepare(false);//主线程不能quit
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
子线程创建需要调用Looper.prepare()
//frameworks\base\core\java\android\os\Looper.java
//sThreadLocal是静态且不可变的,唯一
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//创建前先判断当前线程是否不为空,不为空表示Looper已经创建,保证一个线程只有一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper并保存到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
//libcore\ojluni\src\main\java\java\lang\ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//实际是将Looper保存到对应线程里的map,键值是sThreadLocal
map.set(this, value);
else
createMap(t, value);
}
创建MessageQueue
MessageQueue是一个消息队列,会有队列的一些操作方法。
- 新建队列
在创建Looper的时候会创建一个MessageQueue,一个线程对应一个MessageQueue。由其构造函数和本地方法nativeInit组成,nativeInit会在本地创建一个NativeMessageQueue对象。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
//和当前线程绑定
mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
//是否允许quit,主线程是flase,不允许队列销毁退出。
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
- 元素入队
boolean enqueueMessage(Message msg, long when) - 元素出队
Message next() - 删除元素
void removeMessages(Handler h, Runnable r, Object object)
void removeMessages(Handler h, int what, Object object) - 销毁队列
和创建过程一样通过本地方法nativeDestroy
Looper.loop()循环
public static void loop() {
final Looper me = myLooper();//通过sThreadLocal.get()获取刚才创建的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//消息队列
final MessageQueue queue = me.mQueue;
// 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();
for (;;) {
//死循环,不断从队列读取数据
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
...
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//分发消息,msg.target是绑定的Handler
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
创建Handler
private static Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//处理消息
return false;
}
});
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();//获取Looper
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;
}
创建Message
- 两种方式创建,一种是直接new Message,另外是通过Message.obtain(),obtain()会判断是否可以复用,避免过多的创建、销毁Message对象,优化内存和性能。
- Message是有单链表实现的优先级队列,next保存下个消息。
public static Message obtain() {
synchronized (sPoolSync) {
//sPool就是handler dispatchMessage后通过recycleUnchecked回收复用的Message
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Handler发送消息Message
Handler消息发送方法中由多个重载方法
方法 | 说明 |
---|---|
boolean sendMessage(Message msg) | 发送消息,传入Message参数 |
boolean sendEmptyMessage(int what) | 发送一条空消息 |
boolean sendMessageDelayed(Message msg, long delayMillis) | 发送一条消息 ,消息会在指定的延迟时间后处理 |
boolean sendMessageAtTime(Message msg, long uptimeMillis) | 发送一条空消息,消息会在指定的绝对时间(自系统启动以来的毫秒数)处理 |
boolean post(Runnable r) | 发送一条任务,内部会转成Message |
boolean postDelayed(Runnable r, long delayMillis) | 延迟 发送一条任务 |
最终会调用到enqueueMessage(),通过MessageQueue.enqueueMessage()进行入队。
//frameworks\base\core\java\android\os\Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将handler对象保存到message的target
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用队列的enqueueMessage方法进行发送
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//如果当前队列没有消息或者新消息时间=0,或则时间小于当前队列的消息,将消息插入到最前面
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//判断是否需要唤醒,即异步消息,可以理解成紧急消息
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);
}
}
}
Looper循环分发消息Message
在Looper.loop()中会从当前线程队列取出消息,通过messageQueue.next()获取消息,然后通过msg.target.dispatchMessage(msg)进行分发消息,target就是handler。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//nextPollTimeoutMillis = -1会一直阻塞
// nextPollTimeoutMillis = 0 不会阻塞
//nextPollTimeoutMillis > 0,最长阻塞nextPollTimeoutMillis
int nextPollTimeoutMillis = 0;
//循环查找合适的消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//调用本地的方法进行阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
//从队列头取出消息
synchronized (this) {
// Try to retrieve the next message. Return if found.
//系统开机到当前的时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//msg.target == null表明有设置异步屏障需要找出异步消息进行处理
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//有消息处理,时间如果未到,设置阻塞时间
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.
//链表操作,获取msg,并且删除该节点
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;
}
...
public void dispatchMessage(Message msg) {
//通过post的方式才会不为空
if (msg.callback != null) {
handleCallback(msg);
} else {
//mCallback是构造的时候进行初始化的
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
总结
handler.sendMessage发送消息,Looper.loop轮询取出消息,最后由message绑定的handler进行处理。
一些常见问题分析
一个线程有几个Looper,是如何保证的?
一个线程只能有一个Looper对象
- Looper的构造与获取
私有构造方法:Looper的构造方法是私有的,意味着不能直接在代码中通过new Looper()的方式创建Looper对象。
静态方法获取:为了获取Looper对象,必须使用Looper类提供的静态方法,如Looper.prepare()和Looper.getMainLooper()。Looper.prepare()用于在当前线程中初始化一个Looper对象,而Looper.getMainLooper()则用于获取主线程的Looper对象。 - ThreadLocal的使用
ThreadLocal的特性:ThreadLocal是一个用于存储线程本地变量的类。每个线程都有一个独立的ThreadLocalMap,用于存储该线程的本地变量。这意味着不同线程之间不会互相干扰。
Looper与ThreadLocal的结合:在Looper类中,使用了一个静态的ThreadLocal变量来存储每个线程的Looper对象。由于ThreadLocal的特性,每个线程都会有一个独立的Looper对象,且这些对象之间互不干扰。 - 保证一个线程一个Looper的机制
Looper的初始化:当调用Looper.prepare()方法时,会检查当前线程是否已经有了Looper对象。如果没有,则创建一个新的Looper对象,并将其存储在当前线程的ThreadLocalMap中。
Looper的获取:当调用Looper.myLooper()方法时,会从当前线程的ThreadLocalMap中获取Looper对象。由于ThreadLocal的特性,每个线程都会获取到自己对应的Looper对象。
防止重复初始化:由于Looper的构造方法是私有的,且Looper.prepare()方法会检查当前线程是否已经有了Looper对象,因此可以防止在同一个线程中多次初始化Looper对象。
消息同步屏障机制
消息是放到同个MessageQueue队列中,取消息时是先取头部的消息进行处理,而添加消息也是按时间顺序进行添加,如果同个时间段内的一个消息,需要及时处理就需要同步屏障机制。
同步屏障机制原理
同步屏障机制就是阻碍同步消息,先处理异步消息。
设置同步屏障可以通过MessageQueue.postSyncBarrier()进行设置,
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
//从消息池获取Message
final Message msg = Message.obtain();
msg.markInUse();
//消息初始化的时候没有给msg.target赋值,所以msg.target= null
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//将同步屏障消息插入合适的位置
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
设置完屏障信息后,异步消息的处理是在loop循环时取消失时会进行判断即messagequeue.next()。
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//循环查找合适的消息
for (;;) {
...
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//msg.target表明有设置异步屏障需要遍历循环查找出异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
...
如上图,有了黄色的同步屏障,异步消息就会优先处理。
同步屏障应用场景
在Android系统中,UI刷新的时候发送的消息要及时处理,这时候就会设置异步消息。比如View reuestLayout,invalidate等地方会执行scheduleTraversals。
//frameworks\base\core\java\android\view\ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//设置同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//发送异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
//设置异步消息
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
消息处理完后需要移除同步屏障
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
Handler为啥有可能出现内存泄漏的问题
原因:
如果Handler被定义为Activity的内部类,那么Handler将隐式地持有其外部类(即Activity)的引用。在Handler机制中,每个Message对象都有一个target属性,该属性指向发送该消息的Handler。如果Message在MessageQueue中等待处理的时间过长(例如,由于延迟发送或消息队列拥堵),并且在此期间其关联的Activity已被销毁,但Message仍然存在于MessageQueue中,那么它将继续持有Handler(以及可能的Activity)的引用。
当looper中,如果消息队列没有消息是如何处理的
在没有消息需要处理时通过阻塞Looper线程,可以避免不必要的CPU占用。在enqueueMessage消息时会判断是否需要唤醒。
阻塞是通过本地nativePollOnce(long ptr, int timeoutMillis)
Message next() {
...
for (;;) {
//进行阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
}
...
}
//frameworks\base\core\jni\android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
唤醒
boolean enqueueMessage(Message msg, long when) {
...
if (needWake) {
nativeWake(mPtr);
}
}
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
Message的重复利用-享元模式
//frameworks\base\core\java\android\os\Message.java
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
在Android的Handler机制中,Message对象用于在线程间传递信息。由于Message对象的创建和销毁可能涉及相对昂贵的内存分配和垃圾回收操作,因此Android框架提供了一种机制来重用Message对象,以减少内存开销。Message.recycleUnchecked()方法正是用于这一目的。
当你调用recycleUnchecked()方法时,一个已使用的Message对象会被标记为可重用,并将其状态重置为初始状态(或接近初始状态)。这样,当需要新的Message对象时,可以从一个池中获取这个已重置的对象,而不是创建一个全新的对象。
与享元模式的联系
- 对象重用:Message.recycleUnchecked()方法通过重用对象来减少内存使用,这与享元模式的核心思想是一致的。
- 状态管理:在享元模式中,享元对象的状态被分为内部状态和外部状态。虽然Message对象的状态管理可能不如享元模式那样严格区分内部和外部状态,但重用Message对象时确实需要重置其状态,以确保新使用者不会受到之前状态的影响。
- 性能优化:通过重用对象,Message.recycleUnchecked()方法和享元模式都可以提高性能,减少内存分配和垃圾回收的开销。