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

标签(空格分隔): 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) {
    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("");

        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程序中的Looper.loop()不会造成ANR异常)