主线程Handler中的Looper死循环为何不会导致应用卡死

2020-05-25  本文已影响0人  南山村靓仔

我们都知道主线程不能做太耗时的操作,否则容易发生ANRandroid规定触发条件为:
1)KeyDispatchTimeout(5 seconds) --主要类型按键或触摸事件在特定时间内无响应
2)BroadcastTimeout(10 seconds)--BroadcastReceiver在特定时间内无法处理完成
3)ServiceTimeout(20 seconds)--小概率类型 Service在特定的时间内无法处理完成

估计有人就问了,既然主线程中耗时操作容易发生ANR,那为何主线程中的Looper存在死循环,为什么不会导致应用卡死,出现ANR呢?

那么我们来一步一步将解答这一谜题

1、首先第一步,我们得找到Looper创建的地方,这里不作详细描述,可以去搜索一下关于应用启动流程的文章,这里我先直接告诉大家是在ActivityThreadMain入口函数内,代码如下

public static void main(String[] args) {
       ...
       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");
}

2、我们进入Looploop()函数一探究竟,果然是有个for死循环。我们可以看到死循环里面主要是遍历MessageQueue,若有消息则调用 msg.target.dispatchMessage(msg),否则阻塞在queue.next()方法处

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
       ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
           ...
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          ...
        }
    }

3、接下来我们看看msg.target.dispatchMessage(msg)方法是做了些什么骚操作?首先我们得知道msg.target是啥,我们找到如下代码,可以大概知道这个target实际上就是Message所关联的handler

public final class Message implements Parcelable {
...
Handler target;
...
}
public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

4、msg.target.dispatchMessage(msg)方法实际上调用的就是handler.dispatchMessage(msg)咯,进进入Handler类源码我们可以看到dispatchMessage实际上就是执行了我们平常所回调的handlerMessage方法

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

5、我们回到ActivityThread中的main方法中,我们所设置的handlergetHandler()方法中返回的H类,我们可以看到大量熟悉的消息处理,比如activity的启动、pause、stop、resume等消息处理

 final H mH = new H();
  ...
  final Handler getHandler() {
        return mH;
    }
private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
        public static final int STOP_ACTIVITY_SHOW      = 103;
        public static final int STOP_ACTIVITY_HIDE      = 104;
        public static final int SHOW_WINDOW             = 105;
        public static final int HIDE_WINDOW             = 106;
        public static final int RESUME_ACTIVITY         = 107;
        public static final int SEND_RESULT             = 108;
        public static final int DESTROY_ACTIVITY        = 109;
        public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111;
        public static final int NEW_INTENT              = 112;
        public static final int RECEIVER                = 113;
        public static final int CREATE_SERVICE          = 114;
        public static final int SERVICE_ARGS            = 115;
        public static final int STOP_SERVICE            = 116;

        public static final int CONFIGURATION_CHANGED   = 118;
        ...
        public static final int MULTI_WINDOW_MODE_CHANGED = 152;
        public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
        public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
        public static final int ATTACH_AGENT = 155;
        public static final int APPLICATION_INFO_CHANGED = 156;
        public static final int ACTIVITY_MOVED_TO_DISPLAY = 157;

       
        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, "LAUNCH_ACTIVITY");
                    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");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
               ...
                case STOP_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
                    handleStopService((IBinder)msg.obj);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
}

看到这里,你应该明白了吧?

实际上Loop死循环的目的就在于
1、保持主线程处于一直运行状态
2、通过loop循环遍历MessageQueue,然后通过H handler类的handleMessage方法去处理activity相关的start、pause、stop、destroy等各类事件

这样看来,这个loop死循环的设计非常合情合理,了解了这个机制后,你就不会疑惑为何Looper死循环不会导致应用卡死了吧?

另外,我们也从这个问题知道,loop死循环和ANR是两码回事,ANR是为了避免主线程做过长时间的耗时操作,导致应用卡顿体验差所做的一个限定,超出此限定,那么系统就要发飙了...哈哈,以此限制我们开发过程中,尽量避免在主线程中做耗时操作,而是将耗时操作放到子线程中去

上一篇下一篇

猜你喜欢

热点阅读