为什么Android程序中的Looper.loop()不会造成A
标签(空格分隔): android
版本:3
作者:陈小默
声明:禁止商用,禁止转载
我们在学习Handler的时候一定都接触过Looper这个东西,也知道其中的loop方法会有阻塞等待的过程。
那么问题来了:既然主线程被阻塞了,为什么不会造成ANR异常呢?
首先这个问题就是错误的,至少有两个概念没有认清:第一,什么是ANR异常?;第二,Android程序阻塞的作用是什么?
这里先回答第一个问题:什么是ANR异常。
最简单的话说就是:当前的事件没有机会得到处理
当我们每次点击屏幕就会产生一个事件,这个事件由Android操作系统接收,之后再传递给我们的应用程序。但是我们的Android应用要求我们只能有一个线程去处理事件,这个线程就是我们的主线程。
耗时操作是引起ANR的原因吗?
大部分Android新人都有一个误区,认为只要运行时间长的操作就是耗时操作!是这样吗?我们做一个实验
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById(R.id.sleep)!!.setOnClickListener { view ->
Thread.sleep(10000)
}
}
}
当我们点击按钮的时候,主线程休眠10秒,那么这里会抛出异常吗?答案是
有可能
围观群众:卧槽,啥叫有可能?
当你运行这个程序并点击休眠按钮的时候,如果你不点击屏幕就是保证再没有新的事件输入的时候,这时候是不会抛出异常,并且主线程会安安静静的休眠10秒,但是如果你在点击休眠之后又点击应用界面的任意位置,此时新的事件就会产生并且被输入到你的应用,但是你的主线程正在休眠,而Android又不允许在其他线程中处理UI事件,于是新的事件会被阻塞。当事件超过某一个时间限制(一般Activity是5s)仍未被执行的时候,就会抛出ANR异常。
通过这个例子我们可以明白ANR异常只是单纯指事件长时间未响应。
现在我们回答了第一个问题。接下来看第二个问题:Android程序阻塞的作用是什么?
现在打开你们的记事本,在上面输入一段java或者kotlin代码
fun main(args: Array<String>) {
println("hello world")
}
然后我们去运行这段代码,是不是有了特别了不起的发现,那就是程序运行完之后居然自动退出了。
围观群众:卧槽,程序运行完不退出干啥呀,等过年呀!!!
咳咳,程序运行完成之后退出是程序员的常识问题,可是越是简单的细节就越是容易被忽略。Android程序也是JVM进程呀?它是怎么怎么保证程序不会退出的呢?
围观群众:煞笔呀,只要让程序不结束一直运行就行了呀!!!
对呀,又是常识性问题,想让程序永远不会退出的最好方法就是---循环---还要必须是---死循环---
现在我们看一下一个Android程序是如何被启动的
上源码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这是ActivityThread中整个main方法的代码,就这么多
看这个main方法的第39行代码,此时启动了一个死循环用来保证应用程序不会退出。
围观群众:罗里吧嗦一大堆,我还是没听懂!!!
这么跟你说吧,主线程在没有事件需要处理的时候就是处于阻塞的状态。想让主线程活动起来一般有两种方式:一种是系统唤醒主线程,并且将点击事件传递给主线程;第二种是其他线程使用Handler向MessageQueue中存放了一条消息,导致loop被唤醒继续执行。
围观群众:那也就是说应用的UI线程大部分情况下都是“死的”喽?
嗯,就是这样,我们可以看到的界面炫酷的效果都是子线程与Handler的执行结果,比如播放视频,或者View的动画,里面都用到了Handler。
围观群众:哦,那面试的时候被问道这个问题我应该怎么跟面试官说呢?
直接回答:煞笔,你问的问题有问题知道不
围观群众:滚!!!