概述
马上年底了,年初制定的计划好多都没完成。这一年写了很多东西。但是又都感觉深度不够,所以没有发出来。最近整理了下,准备慢慢发出来。进入正文。
Handler是面试必问系列问题之一。本系列将从初学者的视角分析面试中常见的问题。
handler源码学习(1) — Handler
handler源码学习(2) — Message
handler源码学习(3) — Looper
handler源码学习(4) — MessageQueue
1. 创建Handler
1.1 首先看构造方法
//1
public Handler() {
this(null, false);
}
//2
public Handler(@Nullable Callback callback){
this(callback, false);
}
//3
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//4
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
//5
@UnsupportedAppUsage
public Handler(boolean async) {
this(null, async);
}
//6
public Handler(@Nullable Callback callback, boolean async) {
//FIND_POTENTIAL_LEAKS是一个值为false的常量。按道理是不可能为true,不知道什么时候会变为true
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
//如果getClass是匿名类,或者成员类,或者非静态本地类,警告存在内存泄露的风险
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//创建looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//赋值操作
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//7
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到一共7个构造方法,我都在注释上加了编号,可以发现带looper的最后都是调用了方法7,不带looper的都是调用了方法6。这里都加了具体的注释。只对这几个参数作用说明。
looper:为handler指定looper的线程
mQueue:looper的队列
mCallback:handler处理的回调
mAsynchronous:是否发送异步消息。后面同步屏障会讲。
1.2 静态方法创建
public static Handler createAsync(@NonNull Looper looper) {
if (looper == null) throw new NullPointerException("looper must not be null");
return new Handler(looper, null, true);
}
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
/** @hide */
@UnsupportedAppUsage
@NonNull
public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}
/** @hide */
@NonNull
public static Handler mainIfNull(@Nullable Handler handler) {
return handler == null ? getMain() : handler;
}
前两个都是创建异步hanlder,后边两个是获取main线程的handler。
2.obtainMessage 创建消息
public final Message obtainMessage(){
return Message.obtain(this);
}
public final Message obtainMessage(int what){
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, @Nullable Object obj) {
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2){
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
return Message.obtain(this, what, arg1, arg2, obj);
}
都是调用的Message的静态方法创建一个消息。这和我们直接new Message()有什么区别呢?。先跳过,后续讲message的时候具体分析。
3.发送消息
sendMessagexxx和postMessagexxx,因为post最后要调用
sendMessagexxx,所以我们先看sendMessagexxx
3.1 send消息
再看发送消息,这里我们加上编号。
- 1.sendMessage(Message msg)
- 2.sendEmptyMessage(int what)
- 3.sendEmptyMessageDelayed(int what, long delayMillis)
- 4.sendEmptyMessageAtTime(int what, long uptimeMillis)
- 5.sendMessageDelayed(Message msg, long delayMillis)
- 6.sendMessageAtTime(Message msg, long uptimeMillis)
- 7.sendMessageAtFrontOfQueue(Message msg)
通过观察我们可以看到他们的关系
- 1–>5–>6
- 2–>3–>5–>6
- 4–>6
- 6–>enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
- 7–>enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
这个有2个知识点。
- 我们看到最后都是调用的1,2,3,4,5最后都是调用sendMessageAtTime这个方法。那么发送延时消息是怎么计时的呢?看下源码
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
注意这里SystemClock.uptimeMillis() + delayMillis。SystemClock.uptimeMillis()这个方法返回的官方注解Returns milliseconds since boot, not counting time spent in deep sleep.。个人理解手机启动后未休眠状态的时间。Android 7.0之后手机息屏太久会进入休眠。可以看到会计算出延时后的时间,然后进行入队列。
- sendMessageAtFrontOfQueue如何添加到队头的?
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg){
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, 0);
}
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);
}
可以看到sendMessageAtFrontOfQueue和sendMessageAtTime的区别就是在enqueueMessage的第三个参数不同。实时上,messageQueue入队列就是根据这个时间入队列的。
3.1.1 入队列,进入消息池
看名字就是消息入队列了。看下源码
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//target是Message的成员变量
msg.target = this;
if (mAsynchronous) {
//是否异步,同步屏障再讲这里
msg.setAsynchronous(true);
}
//MessageQueue执行入队列操作
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue的enqueueMessage(msg, uptimeMillis)等我们看MessageQueue时再具体细讲。
3.2 post消息
public final boolean post(@NonNull Runnable r) {
return
sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
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 postDelayed(
@NonNull Runnable r, @Nullable Object token, long delayMillis) {
return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
可以看到分别调用了sendMessageAtTime(@NonNull Message msg, long uptimeMillis)和sendMessageDelayed(@NonNull Message msg, long delayMillis)。和sendMessage唯一不同的地方就是这里的message,这里会调用getPostMessage(Runnable r)和getPostMessage(Runnable r, Object token)。看下其中的一个源码。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
我们再看下dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
明白了吗?如果msg.callBack!=null,则会调用message.callback.run()。我们postMessage,这里会帮我给msg.callback赋值为我们传递的runnable。当分发消息时,就调用这个runnable.run。这就是为什么我们post消息没有调用runnable.run()却可以直接在runnable的run里面的逻辑却可以正常运行。
4.移除回调或者message
public final void removeCallbacks(@NonNull Runnable r) {
mQueue.removeMessages(this, r, null);
}
public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
mQueue.removeMessages(this, r, token);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
public final void removeMessages(int what, @Nullable Object object) {
mQueue.removeMessages(this, what, object);
}
public final void removeCallbacksAndMessages(@Nullable Object token){
mQueue.removeCallbacksAndMessages(this, token);
}
MessageQueue具体分析。
5.总结
纵观Handler一共做了一下几件事
- 创建Handler
- 创建消息
- 发送消息
- 移除回调,或者消息
解决了疑问:
- 有哪些主要方法
- 如何发送延迟消息(如何计算消息触发时间的)
- sendMessageAtFrontOfQueue如何添加到消息队头
- 为什么我们post消息可以直接在runnable的run里处理业务逻辑
同时留下几个问题:
- 如何实现同步屏障
- obtainMessage()和new Message的区别
- queue.enqueueMessage(msg, uptimeMillis);做了什么
最后
以上就是自觉店员为你收集整理的handler源码学习(1) — Handler的全部内容,希望文章能够帮你解决handler源码学习(1) — Handler所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复