美文网首页
主线程Handler中的Looper死循环为何不会导致应用卡死

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

作者: 南山村靓仔 | 来源:发表于2020-05-25 17:20 被阅读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是为了避免主线程做过长时间的耗时操作,导致应用卡顿体验差所做的一个限定,超出此限定,那么系统就要发飙了...哈哈,以此限制我们开发过程中,尽量避免在主线程中做耗时操作,而是将耗时操作放到子线程中去

    相关文章

      网友评论

          本文标题:主线程Handler中的Looper死循环为何不会导致应用卡死

          本文链接:https://www.haomeiwen.com/subject/bziyahtx.html