天天看点

主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

1.引言

众所周知在Activity的主线程中不能做耗时操作,但是 查看ActivityThread的源码可以看到,该线程中包含了一个Loop.looper()的阻塞操作,那么该阻塞操作为何不会引起ANR?

2.源码分析

其实引起ANR的原因主要包括以下两点:

1.当前的事件没有机会得到处理(即主线程正在处理当前事件,没有及时完成或looper中的事件分发处理被阻塞);

2.当前事件正在执行,但没有及时完成。

为了避免ANR的产生,安卓中引入了Handler的处理机制,通过查看ActivtyThread的源码可以看出:

public static final void main(String[] args) {
        ...
        //创建Looper和MessageQueue
        Looper.prepareMainLooper();
        ...
        //开始轮询
        Looper.loop();
        ...
    }
           

查看Looper.loop()方法,该方法的操作与我们熟悉的Handelr处理机制类似,分为两步操作:取出消息和分发消息。

while (true) {
       //取出消息队列中的消息
       Message msg = queue.next(); // might block
       ...
       /根据Message中的target标签,交给对应的Handle处理
       msg.target.dispatchMessage(msg);
       ...
    }
           

因此在ActivityThread的main方法中主要是做消息的循环操作,一旦退出该循环操作,那么当前应用就退出了。

但是该死循环是在主线程中操作,为何不会引起ANR呢?

通过查看ActivityThread的handleMessage的源码可以看出,Android是由事件驱动的,常见的触摸和Activity的生命周期都是运行在Looper.loop()的控制之下,如果该循环停止了,那么整个应用也停止了。

public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
            break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case PAUSE_ACTIVITY_FINISHING:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            ...........
        }
    }
           

通过查看上面的源码可以看出Activity的整个生命周期都是依靠Looper.loop(),在不同的生命周期执行时发送不同的消息,handleMessage接收到不同的Message后,根据case判断进行相应的处理。

由于Activty的执行是遵循一定的生命周期方法的,因此如果某个周期的方法做过多的耗时操作,必然会影响下个周期的执行时间,整个生命周期的执行就会出现卡顿,继而会产生ANR的出现。

并且主线的Looper对于消息的处理时,当子线程有消息发送时才会被唤醒,但子线程没有消息发送时,处于待唤醒状态,因此不会对CPU的性能产生影响。

3.总结

因此:主线程的Looper.loop()中死循环本身不会对Activity产生ANR,除非其消息事件本身的处理存在耗时操作,才会产生ANR.

继续阅读