Android中为何主线程不会由于Looper.loop()里的死循环卡死?

在知乎上的问题,以为很好,就转载过来记录一下。java

Android程序的运行入口是android.app.ActivityThread类的main()方法。(android-23)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");
}

而根据Looper.loop()源码可知里面是一个死循环在遍历消息队列取消息。算法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    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
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

并且并也没看见哪里有相关代码为这个死循环准备了一个新线程去运转,可是主线程却并不会由于Looper.loop()中的这个死循环卡死,为何呢?多线程

举个例子,像Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实如今死循环体外可以执行起来的?

(1) Android中为何主线程不会由于Looper.loop()里的死循环卡死?

这里涉及线程,先说说说进程/线程,进程:每一个app运行时前首先建立一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各类Activity/Service等组件。进程对于上层应用来讲是彻底透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数状况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或经过native代码fork进程。

线程:线程对应用来讲很是常见,好比每次new Thread().start都会建立一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来讲进程与线程除了是否共享资源外,并无本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每一个task都尽量公平的享有CPU时间片

有了这么准备,再说说死循环问题:

对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,咱们是毫不但愿会被运行一段时间,本身就退出,那么如何保证能一直存活呢?简单作法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,经过循环方式不一样与Binder驱动进行读写操做,固然并不是简单地死循环,无消息时会休眠。但这里可能又引起了另外一个问题,既然是死循环又如何去处理其余事务呢?经过建立新线程的方式。

真正会卡死主线程的操做是在回调方法onCreate/onStart/onResume等操做时间过长,会致使掉帧,甚至发生ANR,looper.loop自己不会致使应用卡死。


(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

事实上,会在进入死循环以前便建立了新binder线程,在代码ActivityThread.main()中:架构

thread.attach(false);便会建立一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程经过Handler将Message发送给主线程,具体过程可查看 startService流程分析,这里不展开说,简单说Binder用于进程间通讯,采用C/S架构。关于binder感兴趣的朋友,可查看我回答的另外一个知乎问题:
为何Android要采用Binder做为IPC机制? - Gityuan的回答
 并发

另外,ActivityThread实际上并不是线程,不像HandlerThread类,ActivityThread并无真正继承Thread类,只是每每运行在主线程,该人以线程的感受,其实承载ActivityThread的主线程就是由Zygote fork而建立的进程。

主线程的死循环一直运行是否是特别消耗CPU资源呢? 其实否则,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,经过往pipe管道写端写入数据来唤醒主线程工做。这里采用的epoll机制,是一种IO多路复用机制,能够同时监控多个描述符,当某个描述符就绪(读或写就绪),则马上通知相应程序进行读或写操做,本质同步I/O,即读写是阻塞的。 因此说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。


(3) Activity的生命周期是怎么实如今死循环体外可以执行起来的?

ActivityThread的内部类H继承于Handler,经过handler消息机制,简单说Handler机制用于同一个进程的线程间通讯。

Activity的生命周期都是依靠主线程的Looper.loop,当收到不一样Message时则采用相应措施:
在H.handleMessage(msg)方法中,根据接收到不一样的msg,执行相应的生命周期。

好比收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会经过反射机制,建立Activity实例,而后再执行Activity.onCreate()等方法;
再好比收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。 上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。app

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;
    case STOP_ACTIVITY_SHOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        handleStopActivity((IBinder)msg.obj, true, msg.arg2);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case STOP_ACTIVITY_HIDE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        handleStopActivity((IBinder)msg.obj, false, msg.arg2);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SHOW_WINDOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
        handleWindowVisibility((IBinder)msg.obj, true);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case HIDE_WINDOW:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
        handleWindowVisibility((IBinder)msg.obj, false);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case RESUME_ACTIVITY:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
        handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SEND_RESULT:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
        handleSendResult((ResultData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case DESTROY_ACTIVITY:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
        handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
                msg.arg2, false);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case BIND_APPLICATION:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
        AppBindData data = (AppBindData)msg.obj;
        handleBindApplication(data);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case EXIT_APPLICATION:
        if (mInitialApplication != null) {
            mInitialApplication.onTerminate();
        }
        Looper.myLooper().quit();
        break;
    case NEW_INTENT:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
        handleNewIntent((NewIntentData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case RECEIVER:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
        handleReceiver((ReceiverData)msg.obj);
        maybeSnapshot();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case CREATE_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
        handleCreateService((CreateServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case BIND_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
        handleBindService((BindServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case UNBIND_SERVICE:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
        handleUnbindService((BindServiceData)msg.obj);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    case SERVICE_ARGS:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
        handleServiceArgs((ServiceArgsData)msg.obj);
        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;
    case CONFIGURATION_CHANGED:
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
        mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
        handleConfigurationChanged((Configuration)msg.obj, null);
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        break;
    ............................
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}



主线程的消息又是哪来的呢?固然是App进程中的其余线程经过Handler发送给主线程,请看接下来的内容:


---------------------------------------------------------------------------------------------------------------------------
最后,从进程与线程间通讯的角度,经过一张图加深你们对App运行过程的理解:框架



system_server进程是系统进程,java framework框架的核心载体,里面运行了大量的系统服务,好比这里提供ApplicationThreadProxy(简称ATP),ActivityManagerService(简称AMS),这个两个服务都运行在system_server进程的不一样线程中,因为ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的建立与销毁都是由binder驱动来决定的。

App进程则是咱们常说的应用程序,主线程主要负责Activity/Service等组件的生命周期以及UI相关操做都运行在这个线程; 另外,每一个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有不少线程,好比signal catcher线程等,这里就不一一列举。

Binder用于不一样进程之间通讯,由一个进程的Binder客户端向另外一个进程的服务端发送事务,好比图中线程2向线程4发送事务;而handler用于同一个进程中不一样线程的通讯,好比图中线程4向主线程发送消息。

结合图说说Activity生命周期,好比暂停Activity,流程以下:ide

  1. 线程1的AMS中调用线程2的ATP;(因为同一个进程的线程间资源共享,能够相互直接调用,但须要注意多线程并发问题)
  2. 线程2经过binder传输到App进程的线程4;
  3. 线程4经过handler消息机制,将暂停Activity的消息发送给主线程;
  4. 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再通过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
相关文章
相关标签/搜索