1.Handler简介
转:android 11 Handler消息机制-CSDN博客
我们都知道进程间通信一般是用binder、文件、AIDL等,那么线程间通信一般是如何进行的呢?一般通过Handler消息机制来进行线程间的通信,标准的一个场景就是子线程进行耗时操作(网络、下载等),完成后发送消息去通知主线程更新UI。Handler消息机制中主要有四大成员,它们的主要功能如下:
Handler:消息处理器。可以发送消息Handler.sendMessage()和处理消息Handler.handleMessage()。
Message:消息对象。线程间通信的数据单元,Message可携带数据。
MessageQueue:消息队列。实际上是一个单链表结构(插入、删除比较便利,先进先出方式),主要的功能就是向消息池插入消息MessageQueue.enqueueMessage()和取出消息MessageQueue.next()。
Looper:循环器。通过调用Looper.loop()方法不断从消息队列中取出消息,然后按照target将消息分发给对应的Handler去处理。
Handler的消息机制流程:Handler通过sendMessage()方法发送消息(最终会调用MessageQueue中的enqueueMessage()方法)将消息插入到消息队列中去。Looper中的loop()方法开启死循环,会不断的从消息队列中取出消息(通过调用MessageQueue.next()方法取出消息),然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage()方法分发消息,目标Handler收到消息,调用 handleMessage()方法,接收消息,处理消息。
放一张官方的流程图:
2.流程
2.1.Handler
2.1.1.sendMessage()发送消息
以Handler开始,一个典型的Handler发送消息例子:
public class HandlerActivity extends AppCompatActivity {
private Button button;
private static final int MESSAGE_TEST = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
button = findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();//在子线程中创建消息对象
message.what = MESSAGE_TEST;
textHandler.sendMessage(message);//开启线程,向主线程发送消息
}
}).start();
}
});
}
///Handler对象是在主线程中创建的,所以Handler对象接收到消息之后回调handleMessage()方法对UI做出修改是在主线程进行的
private final Handler textHandler = new Handler(Looper.myLooper()){//android 11之前可以不添加Looper.myLooper()参数,表示隐式使用当前线程关联的looper,android 11之后无参方法被弃用
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MESSAGE_TEST:
button.setText("handler-text");//更新UI
break;
default:
break;
}
}
};
}
在主线程中创建Handler实例,可以看出上面创建Handler实例我们传入了一个Looper.myLooper()方法参数,这个参数可以获取到当前线程所绑定的Looper,所以我们在实例化Handler的同时也获取到了当前线程相关的Looper。
frameworks/base/core/java/android/os/Handler.java
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;//获取Looper
mQueue = looper.mQueue;//获取Looper里的MessageQueue
mCallback = callback;
mAsynchronous = async;
}
在Handler的构造方法里会获取到对应的Looper以及Looper里的MessageQueue对象。每个Handler都会关联一个MessageQueue消息队列,MessageQueue又是封装在Looper对象中,而每个Looper又会关联一个线程,所以线程、MessageQueue、Looper之间是一一对应的关系(后面Looper分析)。
然后通过Handler.sendMessage()方法发送消息,其实Handler提供了好几种发送消息的方法,Handler.post/sendMessage/sendMessageDelayed,这几个方法最终调用的都是Hanlder.sendMessageDelayed()方法。
frameworks/base/core/java/android/os/Handler.java
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {//delayMillis是传入的时间,表示多久之后执行这个消息
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);//当前时间+delayMillis就是Message的执行时间
}
执行Message的时间通过delayMillis决定。如果通过sendMessage()来发送消息,那么delayMillis为0,立刻执行Message;如果是通过sendMessageDelayed()方法来发送延迟消息,那么传入的参数就是延迟时间。后面调用sendMessageAtTime()方法:
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//获取到当前线程的MessageQueue,这个是在Handler的构造方法中赋值的
if (queue == null) {//如果MessageQueue为null,表明当前线程异常,无法处理消息。
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);//插入消息
}
通过调用enqueueMessage方法去插入消息,并传入MessageQueue对象,上文我们也提到对应的MessageQueue对象是在Handler的构造方法里赋值的。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//表明当前Handler,后面分发、处理消息的时候会通过这个来找到对应的Handler
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//通过MessageQueue去插入消息
}
最终通过MessageQueue里的enqueueMessage()方法来插入消息(后面分析)。
2.1.2.dispatchMessage分发消息
frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {//如果Message存在回调方法,优先通过message.callback.run来处理
handleCallback(msg);
} else {
if (mCallback != null) {//如果Handler的mCallback不为null时,优先通过mCallback的handleMessage来处理
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//调用Handler.handleMessage方法来处理message,这个方法默认为空,所以我们一般在子类中重写这个方法去处理消息
}
}
可以看出,如果message和Handler存在callback回调的话,优先调用这两者的callbcak,所以我们可以通过重写上面两个方法去改变分发及处理流程,但是一般情况下我们通常通过重写handleMessage()方法去处理消息。
2.1.3.handleMessage处理消息
接着来看处理message的流程。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
handleMessage()方法一般为空,子类必须重写这个方法去实现消息的自定义处理。
2.2.MessageQueue
MessageQueue主要用于enqueueMessage()插入/next()取出消息,主要来看下这两个重要的方法。
2.2.1enqueueMessage()插入消息
frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//绑定处理消息的handler,不能为null
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {//可能有多个线程同时往这个队列插入信息,所以需要做同步处理
if (msg.isInUse()) {//检查当前消息是否正在被处理,后面消息处理的话,这个值会被置位true,避免消息重复处理
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {//检查looper循环是否被停止
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();//消息处理中,置为true
msg.when = when;//延迟时间,when就是消息可以开始被处理的时间点
Message p = mMessages;//p表示队头消息
boolean needWake;
if (p == null || when == 0 || when < p.when) {//MessageQueue没有消息、当前消息不需要等待(when = 0)、当前消息等待的时间比队列中最前面的消息的等待时间更短,那么让当前消息插入到最队列最前面
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;//如果是阻塞状态则唤醒。
} else {
//如果当前消息的when时间小于当前消息队列中某条消息的when,将消息按时间顺序插入到MessageQueue,退出循环
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {//循环遍历消息,找到比当前消息的when小的消息
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;
}
如果满足以下三种条件之一,则直接将当前消息插入到消息队列的队头
(1)当前MessageQueue中还没有消息
(2)该条消息的when为0,表明消息需要立即执行
(3)该消息等待的时间when比队列中最前面的消息的等待时间更短
否则,则会循环遍历消息队列,通过when(消息执行的时间)来将Message插入到MessageQueue中合适的位置。
2.2.2.next()取出消息
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.
//mPtr是MessageQueue的一个long型成员变量,当队列被dispose的时候其变为0
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration//idle handler的数量,首次都为-1,闲时handler机制.
int nextPollTimeoutMillis = 0;//下一个消息到来前,还需要等待的时间
for (;;) {//
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();//释放那些在处理过程的部分不再需要的引用对象
}
//阻塞操作,除非有消息被插入或者nextPollTimeoutMillis到时间,否则一直阻塞在这
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {//锁住该MessageQueue对象
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();//获取当前时间
Message prevMsg = null;
Message msg = mMessages;//msg表示当前的message
if (msg != null && msg.target == null) {//当msg不为null,并且msg.target为null时,表示此消息为同步屏障消息,循环找到消息队列中的第一条异步消息
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());//只取异步消息,即msg.isAsynchronous为true
}
if (msg != null) {//当前消息非空,且有对应处理handle
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.
mBlocked = false;
if (prevMsg != null) {//有异步消息
prevMsg.next = msg.next;
} else {
mMessages = msg.next;//无异步消息直接取出这个消息,并重置MessageQueue队列的头
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();//标记该消息对象被使用
return msg;//返回将要执行的message
}
} else {//没有消息,一直休眠,等待被唤醒
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {//消息正在退出,返回null
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//重要的参数 pendingIdleHandlerCount,它表示的就是需要执行的空闲任务数,空闲时间要执行的任务
//在进入这个方法的时候pendingIdleHandlerCount的值是-1
//pendingIdleHandlerCount小于0,消息队列为空,或者是消息队列的第一个消息还没有到执行时间。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();//获取idlehandler的数量
}
if (pendingIdleHandlerCount <= 0) {//如果idlehandler数量小于0,结束这次循环,将这个线程挂起等待
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {//如果 pendingIdleHandlerCount不小于零,就把所有需要执行的空闲任务拿出来执行。
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//处理idle handler,只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();//调用idler的queueIdle方法处理,Activity的Destory()方法就是放在这个handler里处理的
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {//停止该空闲Handler的运行
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;//重置pendingIdleHandlerCount为0,防止再次创建和运行空闲Handler
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next()方法开启一个死循环,首先调用nativePollOnce()方法,来判断是否需要进行阻塞操作,当没有消息需要处理、或者还没到消息的处理时间时,则进行休眠操作。后面有消息的时候会进行唤醒操作(enqueueMessage方法中)。然后分别对异步、同步消息进行处理,返回当前的message。最后当在MessageQueue队列里没有需要立即执行的任务时,如果有IdleHandler则会去执行IdelHandler的任务,通过idler.queueIdle()处理任务。IdelHandler里的任务一般是不怎么重要的任务,Activity的onDestory()方法就是在这里完成的。
2.3.Looper
处理消息的永动机,开启loop()循环后不断从消息队列中取出消息,并将消息分发给对应的目标处理者。
2.3.1.prepare()方法
上文提到在主线程使用Looper,那么是否只能在主线程中使用?其实在子线程中我们也可以使用Looper,一个在子线程中使用Looper的例子:
如果我们按照上文中主线程的方式,新建一个子线程,然后在子线程中使用Looper,代码如下:
public class TextThread extends Thread{
public Handler textHandler;
public void run(){
textHandler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
}
}
出现了运行时异常
AndroidRuntime: FATAL EXCEPTION: Thread-2
AndroidRuntime: Process: xxx, PID: xxx
AndroidRuntime: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference
AndroidRuntime: at android.os.Handler.<init>(Handler.java:257)
AndroidRuntime: at android.os.Handler.<init>(Handler.java:162)
AndroidRuntime: at com.example.myapplication.TextThread$1.<init>(TextThread.java:12)
AndroidRuntime: at com.example.myapplication.TextThread.run(TextThread.java:12)
AndroidRuntime: at com.example.myapplication.MainActivity$1$1.run(MainActivity.java:75)
为什么我们可以直接在主线程中使用Looper,但是在子线程中使用就会出现异常呢?是因为主线程默认创建了Looper。
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
......
Looper.prepareMainLooper();//prepare方法用来创建该线程的Looper对象,因为当前是主线程,所以这里创建的是主线程的Looper对象
......
Looper.loop();//启用无限循环
}
frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);//调用prepare方法创建Looper对象,主线程中quitAllowed为false,表示不允许退出。
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//在子线程中使用Looper都要先调用prepare()方法,它还有个同名的public方法,供外部直接调用
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//ThreadLocal确保线中Looper的唯一性,超过一个,则会抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//创建Looper
}
//Looper的私有构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//获取Looper里的MessageQueuue
mThread = Thread.currentThread();//当前线程
}
上文我们提到过在实例化Handler的时候,传入了Looper.myLooper()参数。
frameworks/base/core/java/android/os/Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//获取当前线程所绑定的Looper
}
可以看出在Looper.prepare()方法中通过sThreadLocal来创建Looper,然后实例化Handler的时候通过Looper.myLooper()方法来获取到当前线程绑定的Looper。
2.3.2.loop()方法
回到ActivityThread.main方法里,主线程调用Looper.prepareMainLooper()去创建主线程专属Looer之后又调用了Looper.loop()方法。这个方法是消息处理的核心方法,主要是开启无限循环去处理消息。
frameworks/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();//获取当前线程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");//可以看出使用Looper必须先调用Looper.prepare()方法
}
if (me.mInLoop) {//该线程是否已经调用过loop方法
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
final MessageQueue queue = me.mQueue;//获取Looper里对应的MessageQueue
// 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();//清空远程调用端的uid和pid,确保该线程是基于本地进程的
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);//如果消息调度和传递的时间较长,可以显示一些log,可手动setprop设置
boolean slowDeliveryDetected = false;//用于打印log
for (;;) {//开启循环,不断从消息队列里拿出消息进行处理
Message msg = queue.next(); // might block//调用MessageQueue.netx()获取队列头的消息,前面有分析,这个过程可能会阻塞。
if (msg == null) {//如果获取到的消息为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 Printer logging = me.mLogging;//log打印
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
......
try {
msg.target.dispatchMessage(msg);//msg.target其实是Handler,调用了Handler的dispatchMessage方法分发消息。
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} ......
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
......
msg.recycleUnchecked();//回收消息
}
}
loop()方法主要就是开启了死循环,不断通过调用MessageQueue.next()方法从队列中取出消息,然后进行分发。
这样在主线程中就默认创建了Looper,所以我们可以直接在主线程中直接使用Looper。但是子线程中没有默认创建,所以我们在子线程中使用Looper时需要先调用Looper.prepare()方法启用Looper,还需要调用Looper.loop()方法开启循环去处理消息。
public class TextThread extends Thread{
public Handler textHandler;
public void run(){
Looper.prepare();//创建Looper对象
textHandler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();//开启循环
}
}
3.其他知识点
3.1.Handler、MessageQueue、looper、线程之间的关系
在调用Looper.prepare()方法创建Looper的时候,同一个线程只能创建一个Looper(ThreadLocal确保每个线程只有一个Looper),每个Looper又只有一个MessageQueue,所以MessageQueue、looper、线程之间是一一对应的关系,它们与Handler之间是一对多的关系,因为可以在一个进程里new多个Handler。
3.2.Handler、Looper必须在主线程中使用吗?
不是,子线程中也可以使用,不过子线程中使用Looper需要先调用Looper.prepare()和Looper.loop()方法
3.3Looper为什么不会发生阻塞
我们提到Looper.loop()方法是不断循环操作的,那么会不会发生卡死现象?
不会,looper会根据情况进行阻塞、唤醒操作。
(1)在MessageQueue.next()方法中会首先做阻塞操作nativePollOnce(ptr, nextPollTimeoutMillis);
如果消息队列为null、或者还没到消息处理时间那么进行阻塞,到时间或者插入了消息,那么会被唤醒。
(2)在MessageQueue.enqueueMessage()方法中通过nativeWake()方法唤醒
所以在没有消息产生的时候,looper会被阻塞,线程会进入休眠状态,一旦Looper添加消息,线程就会被唤醒,从而对事件进行响应,所以不会导致卡死。
3.4Handler内存泄露的原因
因为Message会关联发送消息的Handler对象target,如果存在一个延迟消息,那么因为Message还没有没完全处理,即使Activity退出了,message依旧持有handler的引用,handler持有activity的引用,就会造成内存泄露。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/li_5033/article/details/131761630