一、前言
通过这篇文章带大家了解以下内容
- 是否能够理解UI线程的工作机制
- 是否熟悉SurfaceView实现高帧率的原理
二、为什么非UI线程不能更新UI
这个问题我们要从以下几个方面来探讨
- UI线程的工作机制
- 为什么UI设计成线程不安全的
- 非UI线程一定不能更新UI吗
2.1 UI线程是什么怎么来的
我们知道所有的App进程通过 Zygote
fork出来,当我们的App进程启动后会执行 ActivityThread
的main
函数,在main函数里会调用Looper
对象调用loop
函数
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.loop()
进入循环中,所以looper是不会退出的,否则就会抛出异常。这里我们要捋一捋概念,
在Android中,通常来说:
- 主线程 = UI线程
- 其他线程 = 非UI线程
小结一下,UI线程其实就是主线程的别称,他在应用启动时,在ActivityThread执行main
函数被创建。紧接着会调用 Looper
的loop
进入死循环。
这里留一个问题,我们知道Activity的主线程堵塞超过5s会触发ANR,为什么Looper
中的死循环不会出发ANR呢?自后续的文章会为大家揭晓
2.2 主线程消息处理是如何工作
这里简述下整个消息处理工作流程
- Handler通过sendMessage 发送Message到MessageQueue队列
- Looper通过loop(),不断提取出到达触发条件的Message,并将message交给target来处理
- 经过dispatchMessage后,交回给handler的handlerMessage来进行相应的处理
- 将message加入MessageQueue时,往管道写入字符,可以唤醒loop线程;如果MessageQueue中没有Message,并处于idle状态,则会执行idelHandler接口中的方法,往往用于做一些请理性地工作。
2.3 UI为什么不设计成线程安全的
- UI具有可变性,甚至是高频可变性
UI刷新比如游戏可能需要达到60帧,这样频繁的加锁十分影响性能
- UI对响应时间的敏感性要求UI操作必须高效
- UI组件必须批量绘制来保证效率
所以,UI不考虑设计成线程安全的
2.4 非UI线程一定不能更新UI吗
大多数情况下,我们逻辑的运行,比如网络IO,本地文件IO都处理在非UI线程中,为了提高UI线程的响应时间和响应速度