您的位置:首页 > 移动开发 > Android开发

为什么Android程序中的Looper.loop()不会造成ANR异常

2016-08-19 17:49 651 查看

为什么Android程序中的Looper.loop()不会造成ANR异常

标签(空格分隔): 菜鸟 android

作者:陈小默

我们在学习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。

围观群众:哦,那面试的时候被问道这个问题我应该怎么跟面试官说呢?

直接回答:煞笔,你问的问题有问题知道不

围观群众:滚!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 线程 Loop ANR main
相关文章推荐