Handle、Message、Looper
Handle、Message、Looper
Handler
对于Handler.sendMessage(Message msg)或者sendEmptyMessageDelayed(int what,long delayMills)来说,其实最终都是调用到了
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
从前面的代码分析,这里的queue就是一个MessageQueue类型的变量,都来自于一个全局变量mQueue。在Handler的构造函数中,如果没有传入Looper的话,默认的mQueue是来自于mLooper之中,而mLooper又是来自Looper .myLooper();
主要的两个构造函数入下:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = 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());
}
}
mLooper = Looper.myLooper();
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这两个构造函数都是hide的,所以都是通过其余几个构造函数传递到这两个构造函数里来,默认async是false
从上面的代码分析
Looper中的myLooper方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
或者是直接调用myQueue方法获取MessageQueue类型获取Looper中的变量mQueue
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
而该MessageQueue类型的变量mQueue来自Lopper中的prepare()方法
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
ThreadLocal其实就是一个每个Thread都有的存储空间,如果需要往里面放入数据,则调用其中的set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
每个Thread对象中都默认有一个null的threadLocale
ThreadLocal.ThreadLocalMap threadLocals = null;
Looper
looper的静态方法prepare会new一个Looper(同时new一个MessageQueue)并放入每个Thread中所包含的ThreadLocal,相当于每个Thread中都能存储一个Looper
而looper的静态方法loop(),则是会进行一个死循环,一直从该looper中的MessageQueue中来取出首位的msg,并调用
msg.target.dispatchMessage(msg);
而msg.target方法是我们在使用Handler.sendMessage等系列方法时指定的,如最上面的第一段代码,msg.target就是我们调用时指定的Handler,因此looper会首先去调用对应handler的dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中msg的callback变量是一个Runnable,如果在创建Message的时候没有指定,则默认为null,如果指定了,则是直接调用该msg.run()
private static void handleCallback(Message message) {
message.callback.run();
}
若没有,则是调用创建Handler时的构造函数中的Handler.CallBack实现,或者是我们平常使用的new Handler对象
以下总结来自于Android异步消息处理机制
- 首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
- Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
- Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
- Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
- 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法
如下是大神写的一个关于使用Handler的例子
Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
推荐阅读
-
java调用sqlldr报错:Message 2100 not found
-
android开发教程之handle实现多线程和异步处理
-
Looper中的睡眠等待与唤醒机制
-
Android中的Looper对象详细介绍
-
android开发教程之使用looper处理消息队列
-
利用Jmail.Message发送邮件
-
message() 信息提示
-
ORA-27300: OS system dependent operation:sendmsg failed with status: 105 ORA-27301: OS failure message: No buffer space available
-
Appium新版本遇到的问题,不能通过 name 去定位元素抛 Message: Locator Strategy 'name' is not supported for this session
-
优化 Git Commit Message