一,Handler的使用
1)创建全局静态常量
class Cons{
static final int MSG_WHAT_0x1000 = 0x1000;
static final int MSG_WHAT_0x1001 = 0x1001;
}
2)创建Handler对象
private static Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what){
case MSG_WHAT_0x1000:{
Log.d("MainActivity","show message-->"+msg);
}
case MSG_WHAT_0x1001:{
}
break;
default:
throw new IllegalStateException("Unexpected value: " + msg.what);
}
return false;
}
});
3)Handler消息的发送
handler.sendMessage(handler.obtainMessage(MSG_WHAT_0x1000));
4)移除消息
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
如上便是谷歌官方推荐的Handler使用方式,上述代码可能对一些新手有一些疑问,在此罗列(大佬请绕道):
Q1,创建Handler时使用了“new Handler.Callback()”而不是如下方式创建:
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
Q2,创建Handler对象时,使用了static 对handler对象的修饰
Q3,发送消息时使用了“handler.obtainMessage”而不是直接new
Q4,在onDestroy中调用removeCallbacksAndMessages
接下来就带着以上疑问进入源码分析。
二,Handler源码流程分析
Handler的源码分析在Framework层目前主要涉及到四个类:Handler,Message,MessageQueue,Looper。
1,Looper及MessageQueue对象创建
在AndroidStudio中多次Shift键或者在线源码找到ActivityThread.java类,它便是Android的主线程也称为UI线程,Looper的初始化便在其main函数中
//见源码7310行
public static void main(String[] args) {
//省略若干代码
(1)此处调用了创建了Looper对象
Looper.prepareMainLooper();
//省略若干代码
(2)开启消息的轮询
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
根据(1)处的代码,进入到Looper类的prepareMainLooper函数中
//见Looper源码119行
public static void prepareMainLooper() {
//调用内部函数进行Looper对象的创建
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个Looper对象,并存入到ThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
//创建了一个MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
上述便是Looper的创建过程了,创建了主线程中唯一的Looper对象,并存入到了ThreadLocal,在其构造函数中并创建了一个MessageQueue的消息队列。
关于ThreadLocal,它内部有一个ThreadLocalMap,类似与HashMap的数据结构,其键是当前ThreadLocal,值是要存放的属性。
在Thread.java源码中可以看到其内部有一个 ThreadLocal.ThreadLocalMap threadLocals = null;的内部变量,而我们创建的ThreadLocal最后都会存放到该Map中。
ThreadLocal存放值时其源码如下:
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为空,则直接存入当前值
if (map != null)
map.set(this, value);
else
//创建一个ThreadLocalMap并存入当前线程中
createMap(t, value);
}
2,开启Looper的轮询
在ActivityThread的main函数中可知,在创建完成Looper后,又调用了Looper.loop()函数,主要目的就是开启消息的轮询。
进入Looper的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中的MessageQueue,在Looper创建时,会连带创建一个MessageQueue
final MessageQueue queue = me.mQueue;
//开启死循环
for (;;) {
//(1)调用MessageQueue的函数不停的从消息队列中取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//省略...
try {
//(2)调用Message中的target(即Handler对象)进行消息的消费
msg.target.dispatchMessage(msg);
}
//省略...
}
}
(1)MessageQueue中的next函数一探究竟,发现其内部也是一个死循环,并返回一个Message对象。
Message next() {
//死循环,从队列中取出消息进行消费
for (;;) {
//省略...
}
}
如上只是消息的消费的过程,接下来,进入分发过程。
3,Handler消息的发送
根据示例代码中,创建一个消息时,都会调用handler对象的send或post函数,进入handler源码。
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
从上诉代码可知,不管是send或者post的消息最后都调用了enqueueMessage函数
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//关键代码,把当前handler对象赋值给Message
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//把消息按照执行时间存放到MessageQueue中
return queue.enqueueMessage(msg, uptimeMillis);
}
进入到enqueueMessage中,发现我们发送的消息,最后都会调用msg.target = this 把当前Handler对象存放到Message中,在上述Looper的loop轮询中调用的msg.target.dispatchMessage(msg);,就是此时存入的handler对象。
接下来我们就进入Handler中的dispatchMessage函数
public void dispatchMessage(@NonNull Message msg) {
//判断Message中的runable对象是否为空
if (msg.callback != null) {
handleCallback(msg);
} else {
//此处就回到了第一章节中的Q1,它首先判断了我们传入的callback不为空,先调用callback的接口实现,
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//如果callback为空的话,调用内部重写函数,即传统的使用方式,最后执行。
handleMessage(msg);
}
}
到此整个Handler的流程大概跑通了
4,Message源码解析
public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
/**
* Optional Messenger where replies to this message can be sent. The
* semantics of exactly how this is used are up to the sender and
* receiver.
*/
public Messenger replyTo;
/**
* Indicates that the uid is not set;
*
* @hide Only for use within the system server.
*/
public static final int UID_NONE = -1;
/**
* Optional field indicating the uid that sent the message. This is
* only valid for messages posted by a {@link Messenger}; otherwise,
* it will be -1.
*/
public int sendingUid = UID_NONE;
/**
* Optional field indicating the uid that caused this message to be enqueued.
*
* @hide Only for use within the system server.
*/
public int workSourceUid = UID_NONE;
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
* is delivered and afterwards when it is recycled. The flag is only cleared
* when a new message is created or obtained since that is the only time that
* applications are allowed to modify the contents of the message.
*
* It is an error to attempt to enqueue or recycle a message that is already in use.
*/
/*package*/ static final int FLAG_IN_USE = 1 << 0;
/** If set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
/** Flags to clear in the copyFrom method */
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
@UnsupportedAppUsage
/*package*/ int flags;
/**
* The targeted delivery time of this message. The time-base is
* {@link SystemClock#uptimeMillis}.
* @hide Only for use within the tests.
*/
@UnsupportedAppUsage
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public long when;
/*package*/ Bundle data;
@UnsupportedAppUsage
/*package*/ Handler target;
@UnsupportedAppUsage
/*package*/ Runnable callback;
//单链表
/*package*/ Message next;
/** @hide */
public static final Object sPoolSync = new Object();
//缓存池
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
public static Message obtain() {
synchronized (sPoolSync) {
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();
}
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
如上代码,可知Message其是一个单链表的数据结构。
三,调用流程图
image.png其执行流程如下:
image.png
四,答疑
Q1,在已在dispatchMessage源码进行进行解答,这也就是为啥谷歌官方推荐使用callback的方式创建handler
Q2,在创建Handler是,大多时候都是使用的内部类的方式创建,而我们使用Handler时,基本都是在Activity/Fragment中使用的,内部类持有了上下文,会导致内存泄漏的问题,因此使用static进行修饰,当然也可以使用弱引用的方式。
Q3,发送消息时使用了“handler.obtainMessage”而不是直接new?
在Message的源码中,我们发现存在缓存池的概念,调用obtainMessage时,会先从缓存池中取,如果取不到则进行创建并加入缓存池中,这里使用到了设计模式中的享元设计,类似于RecyclerView的复用机制,因为大量的创建Message对象,存在资源浪费的情况,复用Message,可以节约内存开销。
public final Message obtainMessage()
{
return Message.obtain(this);
}
Q4,在onDestroy中调用removeCallbacksAndMessages
答:主要是在页面销毁时,即时的回收消息,防止内存泄漏。
五,面试题解析
1, Looper 的 loop() 死循环为什么不卡死?
为了让主线程持续处理用户的输入,loop() 是死循环,持续调用 MessageQueue#next() 读取合适的 Message。
但当没有 Message 的时候,会调用 pollOnce() 并通过 Linux 的 epoll 机制进入等待并释放资源。同时 eventFd 会监听 Message 抵达的写入事件并进行唤醒。
这样可以空闲时释放资源、不卡死线程,同时能持续接收输入的目的。
彩蛋1:loop() 后的处理为什么不可执行
因为 loop() 是死循环,直到 quit 前后面的处理无法得到执行,所以避免将处理放在 loop() 的后面。
2,异步 Message 或同步屏障
异步 Message:设置了 isAsync 属性的 Message 实例
可以用异步 Handler 发送
也可以调用 Message#setAsynchronous() 直接设置为异步 Message
同步屏障:在 MessageQueue 的某个位置放一个 target 属性为 null 的 Message,确保此后的非异步 Message 无法执行,只能执行异步 Message
原理:当 MessageQueue 轮循 Message 时候发现建立了同步屏障的时候,会去跳过其他 Message,读取下个 async 的 Message 并执行,屏障移除之前同步 Message 都会被阻塞
总结:异步消息一般是系统内部使用的,当handler收到异步消息时,会优先处理异步消息,等到异步消息处理完后,才会处理同步消息,例如vsync信号
3,IdleHandler 空闲 Message
适用于期望空闲时候执行,但不影响主线程操作的任务
系统应用:
Activity destroy 回调就放在了 IdleHandler 中
ActivityThread 中 GCHandler 使用了 IdleHandler,在空闲的时候执行 GC 操作
App 应用:
发送一个返回 true 的 IdleHandler,在里面让某个 View 不停闪烁,这样当用户发呆时就可以诱导用户点击这个 View
将某部分初始化放在 IdleHandler 里不影响 Activity 的启动
网友评论