路漫漫其修远兮,吾将上下而求索
Handler的使用场景
-
子线程操作完成之后,通知主线程执行操作
首先在主线程创建一个Handler实例
private val MSG_WHAT: Int = 1000 private var mHandler = object : Handler() { override fun handleMessage(msg: Message?) { when (msg?.what) { MSG_WHAT -> { val isMainThread = msg.obj as Boolean if (isMainThread) { showToast("what = 1000, 主线程") } else { showToast("what = 1000, 子线程") } } else -> showToast("default") } } }
然后在子线程操作(这里的操作是判断该线程是否是主线程)完成之后,通知主线程:
private fun checkIsMainThread() {
thread {
val message = Message()
message.what = MSG_WHAT
message.obj = isMainThread()
mHandler.sendMessage(message)
}.start()
}
- 判断执行代码的线程是否是主线程
有时候我们需要判断当前线程是否是主线程,这个时候就可以用带Looper了,通过Looper,我们就可以知道当前线程是否为主线程:
private fun isMainThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
-
在子线程中直接切换到主线程执行代码
开发中经常遇到的一个场景就是在子线程中执行一段操作之后(比如网络请求,IO),然后在主线程中更新UI,但是又没有Context,也不想在新建一个Handler的Field接收消息,这个时候就可以利用Handler的构造方法了,在构造方法中传入一个Looper参数,这里获取的是主线程的Looper实例:
private fun directUpdateUI() {
thread {
Thread.sleep(1000)
Handler(Looper.getMainLooper()).post {
btnDirectUpdateUI.text = "UI已经更新了"
}
}.start()
}
当然,这个方法也可以用在两个子线程之间的通信上。
-
runOnUiThread(Runnable action)方法
在Activity中,系统为我们提供了一个一个runOnUiThread方法,可以方便的在主线程中执行操作,而这个方法本质上也是通过Handler进行的操作:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
这里的mHandler是在主线程中创建的,如果当前线程不是主线程,则利用主线程的Handler进行post操作,如果是主线程,则直接执行。
源码解析
-
Looper.prepare()
在创建Handler实例之前,必须要先有Looper实例,至于我们平时在主线程为什么没有显式调用这个方法,前面已经说过了。这里来简单的看下源码:static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static void prepare() { prepare(true); } 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)); }
这里首先看看Looper中的ThreadLocal中有没有存Looper实例,如果没有,则新建一个该线程的Looper实例。ThreadLocal可以看做是一个存储线程私有变量的数据结构,简单的就get和set方法,这里不详述了。
-
创建Handler
一般的用法是在主线程中创建Handler:
val handler = object : Handler() { override fun handleMessage(msg: Message?) { //接收到message之后执行的操作 } }
这里如果要接收Message并执行一定的操作,就必须重写handleMessage方法,因为Handler源码中这个方法是一个空方法。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
然后来看一下Handler的构造函数:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//省略部分代码
//获取当前线程的Looper
mLooper = Looper.myLooper();
//如果当前线程没有Looper,抛出异常
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;
}
从上面代码可以看出,如果要在一个线程中创建Handler,一定要先执行Looper.prepare()方法,创建一个Looper实例,不然会抛出异常。
这里可能会有一个疑问,我们在主线程中创建Handler的时候并没有执行Looper.prepare()方法啊,为什么可以正常执行?
答案是主线程其实在很早之前就已经创建了Looper实例,具体的可以参考ActivityThread这个类中的main方法,具体的这里就不讲了:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//在这里创建主线程Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始消息队列循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
上面代码中创建主线程的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();
}
}
正如前面所说,我们还可以传入一个Looper实例到Handler的构造函数中,用来直接切换到主线程执行,或者切换到别的线程执行代码,来看看源码:
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
就是将传入的Looper实例赋值给mLooper。mQueue则是取得mLooper的消息队列。
- Looper.loop()
创建完Handler实例之后,需要执行Looper.loop()方法,开始从消息队列中取出消息,并分发给对应的Handler进行处理。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
这里从Looper持有的消息队列对象中循环取出消息,然后利用msg.target.dispatchMessage(msg)进行消息分发,这里的msg.target其实就是Handler,然后执行Handler的dispatchMesage方法.
-
消息分发
看看dispatchMessage的源码:
/** * Handle system messages here. */ public void dispatchMessage(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(); }
如果Message自身的callback不为空,即Message的Runnable不为空,则执行Runable,否则判断Handler的Callback是否为空,不为空则执行Callback的handleMessage方法,再根据返回值判断是否执行Handler自身的handleMessage方法。
总结
从一个非主线程创建Handler对于分析Handler的流程将会更加直观,这里梳理一下流程:
- 利用Looper.prepare()初始化Looper
- 创建Handler,其中Handler构造方法可以传入相关参数实现一些特殊的功能
- 执行 Looper.loop()方法,开始从消息队列中取出消息,利用Handler的dispatchMessage方法进行分发
- Handle通过post,sendMessage等方法,发送消息,其实就是将Message添加到MessageQueue中
- Handler通过handleMessage方法处理接收到的Message
网友评论