一、异步消息处理机制主要由以下部分组成:
Handler、Looper、MessageQueue、Message
二、Handler的用途
1.延时处理任务
2.线程间通信(包括在子线程中进行耗时操作,在主线程更新UI)
三、异步消息处理流程
1.Handler通过sendMessage()发送message到消息队列MessageQueue;
2.looper从MessageQueue中循环取出消息,并将message交给对应的target hanler;
3.hanler回调dispatchMessage方法收到消息后进行消息的处理。
PS:一个线程只有1个Looper(通过ThreadLocal来保证)、1个MessageQueue和多个message、但想 new 多少个 Handler 都行 ,一个进程有多个线程
在onCreate中创建个子线程更新UI不会发生异常,onResume时才会发生异常。 因为异常的检测处理在 ViewRootImpl 中,该实例的创建和检测在 onResume() 之后进行。 是否能在子线程更新UI取决于ViewRootImpl在哪个地方创建的
handler问题:
1.为什么Looper.loop()不会导致ANR?
ANR是消息处理超时,应用程序无响应。而Looper.loop()是循环从消息队列取消息,且在没有消息时会休眠,有消息时唤醒并处理消息。造成ANR的原因不在于循环,在于处理消息处理时间过长。 (MessageQueue的休眠和唤醒机制正是基于Linux的epoll机制实现的)
Handler如何实现线程切换?
- 发送消息的线程与处理消息的线程拥有不同的Looper
- 通过ThreadLocal保证线程隔离
MessageQueue的阻塞唤醒机制?
- 基于epoll机制实现
- nativePollOnce()和nativeWake()
2. 如何优化Handler使用?
- 复用Message对象
Message msg = handler.obtainMessage(MSG_UPDATE_UI, "新数据");
handler.sendMessage(msg);
- 及时移除不需要的消息
- 使用合适的作用域
3. Handler的post(Runnable)和sendMessage()有什么区别?
// 实际上post(Runnable)内部也是转换成Message
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
post(Runnable):简单的UI更新、延迟任务
sendMessage():复杂的消息传递,需要区分消息类型(可以携带what、arg等数据)
4. 如何保证在子线程中更新UI的安全性?
// 方案1:使用Activity的runOnUiThread
runOnUiThread(new Runnable() {
@Override
public void run() {
// 这里在主线程执行,可以安全更新UI
textView.setText("来自子线程的数据");
}
});
// 方案2:使用View的post
textView.post(new Runnable() {
@Override
public void run() {
textView.setText("安全更新UI");
}
});
// 方案3:使用自定义Handler
private Handler mainHandler = new Handler(Looper.getMainLooper());
// 在子线程中
new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时操作
final String result = doBackgroundWork();
// 通过Handler切换到主线程
mainHandler.post(new Runnable() {
@Override
public void run() {
textView.setText(result);
}
});
}
}).start();
**************************************************
### 进阶方案:使用LiveData + ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<String> uiData = new MutableLiveData<>();
public LiveData<String> getUiData() {
return uiData;
}
public void loadData() {
new Thread(() -> {
String result = fetchDataFromNetwork();
// LiveData会自动切换到主线程
uiData.postValue(result);
}).start();
}
}
// Activity中观察
viewModel.getUiData().observe(this, new Observer<String>() {
@Override
public void onChanged(String data) {
// 自动在主线程执行
textView.setText(data);
}
});
5.如何通过Handler优化页面启动速度?
6.主线程的looper和子线程looper有什么区别?
主线程 Looper 和子线程 Looper 在 源码层面只有两处差异,其余代码完全一样;差异 1 是“谁帮我 prepare” ,差异 2 是“谁帮我做死循环兜底” 。
主线程 Looper 由系统在 ActivityThread 里 prepare + loop,循环永不退出,异常即杀进程;子线程 Looper 必须手动 prepare/loop,可随时 quit,生命周期自己管。