一、概述
对于Android开发者而言,我们处理异步消息用的最多的也是轻车熟路的一种方式,就是使用Handler进行消息的分发和处理。但是我们在一个页面(Activity 或者 Fragment)中可以直接使用Handler进行消息的分发和处理。实例如下:
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//处理消息
return false;
}
});
我们一般在handleMessage()方法里进行消息的处理,同时在需要发送消息的地方使用mHandler.sendEmptyMessage(0)发送消息即可。callback回调是怎么收到消息的呢?同时Handler又是怎么发送消息的呢?我们带着问题看一下源码。
Android开发者们都知道:当APP启动时,会默认产生一个主线程(也就是UI线程),这个线程会关联一个消息队列,然后所有的操作都会被封装成消息后在主线程中处理。那么到底Android中消息机制是什么样子的呢?
对于Android程序而言,运行程序也是需要通过Java中的程序入口开始执行。而在ActivityThread中的就存在这么一个main方法,作为程序的入口。我们看一下源码:
public static void main(String[] args) {
//省略部分代码…
Process.setArgV0("<pre-initialized>");
//创建主线程Looper
Looper.prepareMainLooper();
//省略部分代码…
ActivityThread thread = new ActivityThread();
thread.attach(false);
//创建主线程对应Handler
if (sMainThreadHandler == null) {//UI线程的Handler
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始消息轮询
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在应用程序中,当执行ActivityThread中的main方法之后,程序就运行起来了。如上面main()方法中的代码所示,首先是通过 Looper.prepareMainLooper();创建主线程的Looper对象,然后通过sMainThreadHandler = thread.getHandler()创建了Handler,也就是ActivityThread中的类H,该类继承于Handler,我们在这里不在分析。最后通过Looper.loop()方法开始了消息的轮询。业务逻辑就这么简单。
二、消息轮询器:Looper
我们通过第一部分的代码发现对于Android消息机制而言,Looper扮演者非常重要的角色。那么我们就像了解一下Looper有哪些特性吧。
首先我们先了解一下Looper的成员变量吧,mQueue是消息队列(MessageQueue),主要用来存储消息。
mThread:是指当前的线程,严格的是与Looper关联的线程。sThreadLocal:主要用来获取或者设置Looper对象。主要就说这三个变量吧。
下面我们说一下它的构造器:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造器中,初始化了消息队列和当前线程的信息,仔细的同学可能注意到了,这里构造器的参数quitAllowed是什么意思呢?我们通过查看MessageQueue的源码我们可以发现,主要用于限制能否安全清除所有消息。若为主线程不允许使用quit方法。
接下来我们了解一下它的主要方法。在第一部分我们看到主线程中,首先使用了Looper.prepareMainLooper()方法创建了主线程的Looper对象。下面我们分析一下源码:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
我们看到在这里使用了prepare(false)方法传入了false,我们看一下源码:
private 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));
}
通过以上代码我们通过sThreadLocal获取当前线程的Looper对象,若已经存在了则会抛出异常*“Only one Looper may be created per thread”,*也就是说一条线程对应一个Looper对象。若没有调用过该方法,则会通过sThreadLocal保存一个Looper对象。我们回过头来,prepareMainLooper()方法,通过设置quitAllowed = false,来创建一个不允许终止的Looper对象,也就是*主线程是不允许调用********quit********方法的*,这样我们在主线程创建的Handler就可以一直进行消息轮询了~。sMainLooper 就是主线程对应的Looper对象。
同时还有一个方法prepare(),该方法主要用于子线程创建Looper,使用Handler();同时建议在子线程中调用了prepare()方法之后需要调用loop()方法,最后以quit()方法结束本次轮询;
最后,我们看一下消息轮询的逻辑吧,也就是loop()方法。源码如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获取Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取looper对象对应的消息队列
final MessageQueue queue = me.mQueue;
//省略部分代码...
//循环
for (;;) {
//若存在,从消息队列中获取消息对象,否则会导致阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//省略部分代码...
//进行消息的分发处理
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//省略部分代码...
msg.recycleUnchecked();
}
}
该方法是Handler 消息机制的核心方法,首先该方法会获取当前线程对应的looper对象MessageQueue的对象,然后就会进入一个*死循环*。在该死循环中首先是通过消息队列获取下一条消息,也就是源码中的“queue.next()”方法,该方法的源码在这里就不在多余解释了,它的作用我们不看源码也能猜得出来,哈哈,但是如果消息队列中没有消息,就会发生阻塞
我们继续向下阅读,我们看到“msg.target.dispatchMessage(msg);”这就是消息分发的调用方法,我们知道msg就是Message的对象,那么target又是啥啊?哈哈,看过Message源码的同学一定都知道:它里面有一个成员变量target,原来是Handler,现在一切都清楚了吧!我们的Handler就是这么收到消息的。
三、纽带:Handler
我们分析完了Looper的源码,作为消息的轮询器Looper扮演着很重要的角色,也可以说是是整个消息机制的动力核心,是Looper驱动着消息的传递。而Handler扮演着纽带的角色,它承载着Looper ,连接着MessageQueue,进行消息的输入输出,我就叫它为纽带吧。
在第一部分我们通过使用Callback将消息回调回页面进行处理,我们先看一下Callback吧。
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
Callback就是Handler暴露给外部的数据接口,用于接收分发的消息,我们就可以在handleMessage()方法里进行处理了。在第二部分中我们分析Looper的loop()方法发现通过“msg.target.dispatchMessage(msg)”方法回调回Handler,我们看一下dispatchMessage()的源码:
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在该方法里首先是判断msg.callback是否为空,我们通过源码知道callback实际上就是一个Runnable对象,若非空的话,就会执行handleCallback(msg)方法,该方法就是让msg.callback这个线程运行起来。若msg.callback为空的话,然后看Handler的成员变量mCallback是否为空不为空的话就会回调Callback的handleMessage()方法;若mCallback为空的话就会调用Handler的handleMessage()方法。现在清楚了吧,想要处理消息要么通过Callback回调,要么实现Handler并重写handleMessage()方法。这就是为啥Handler有一个没有方法体的handleMessage()方法。
我们再看一下消息的添加的逻辑吧。在第一部分我们就是使用mHandler.sendEmptyMessage(0)将消息添加进消息队列的,这类的方法有以下几个:
这类方法都会在一定的时间的情况下,将消息推送在消息队列的底部。然后会在与该Handler关联的线程里的handleMessage方法下处理该消息。
还有种情况就是我们有时候会使用mHandler.post(r),这类方法一般是开了一条子线程去运行,这又是怎么回事呢?首先这类方法有以下几种:
我们看到该类型的方法其参数都包含Runnable参数,当我们调用这类方法时,所添加Runnable就会被添加进相应的消息队列中,该线程最终会在Handler的dispatchMessage()方法中执行,我们知道对于Message的callback就是一条线程,当callback非空时就会运行;
总体上上述方法:
- post()和postDelayed()方法就会通过调用sendMessageDelayed()方法将线程添加进队列,同时在延迟一定时间运行;
- 同时postAtTime()方法是调用sendMessageAtTime()方法将该线程添加进消息队列,然后在指定的时间运行;
我们继续查看源码发现post(),postDelayed()和postAtTime()方法最终都是通过sendMessageAtTime()方法将消息添加进消息队列。我们看一下源码:
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
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);
}
该方法最终通过enqueueMessage()方法将消息添加进入消息队列,随后就会通过Looper将消息分发下去。
postAtFrontOfQueue()方法是通过sendMessageAtFrontQueue()方法将消息添加的消息队列,并保证下一个就会执行。然而最终也是通过enqueueMessage()方法完成的将消息添加进消息队列的操作。关于消息添加进入消息队列的逻辑在这里不在分析了,感兴趣的同学可以看一下MessageQueue的enqueueMessage()方法。
同时在我们的开发中多数情况下页面是会销毁的,这就需要销毁一些消息,有一个方法就能解决这个问题:
removeCallbacksAndMessages()该方法提供了一种解决方法,当Object为null是,消息队列中的消息会被标记回收。具体可以去MessageQueue的removeCallbacksAndMessages()方法中分析。
现在我们理解了为什么在子线程中需要这么使用了吧。
class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }
四、小问题
给大家留个小问题,为什么在Activity中就不需要创建Looper了?
作者:心灵行者
转载来源于:https://blog.csdn.net/zxm528/article/details/104742619
如有侵权,请联系删除!
网友评论