1. 程式人生 > >Android中為什麽主線程不會因為Looper.loop()方法造成阻塞

Android中為什麽主線程不會因為Looper.loop()方法造成阻塞

launch google blog 很多 isp android oid 啟動 組件

很多人都對Handler的機制有所了解,如果不是很熟悉的可以看看我
如果看過源碼的人都知道,在處理消息的時候使用了Looper.loop()方法,並且在該方法中進入了一個死循環,同時Looper.loop()方法是在主線程中調用的,那麽為什麽沒有造成阻塞呢?
首先我們需要從Android程序啟動的入口開始來看:
技術分享圖片
如果不清楚Android的應用啟動詳細流程的可以看看這個
然後再看看Looper.loop()方法:

public static final void loop() {  
    Looper me = myLooper();  
    MessageQueue queue = me.mQueue;  
    // 開始了死循環
    while (true) {  
        Message msg = queue.next(); // might block  
        if (msg != null) {  
            if (msg.target == null) {  
                return;  
            }  
            if (me.mLogging!= null) me.mLogging.println(  
                    ">>>>> Dispatching to " + msg.target + " "  
                    + msg.callback + ": " + msg.what  
                    );  
            msg.target.dispatchMessage(msg);  
            if (me.mLogging!= null) me.mLogging.println(  
                    "<<<<< Finished to    " + msg.target + " "  
                    + msg.callback);  
            msg.recycle();  
        }  
    }  
}

這麽一看,似乎真的是在主線程中有一個死循環,而且沒有造成阻塞?

那麽我們先從入口ActivityThread 類開始看:首先 ActivityThread 並不是一個 Thread,就只是一個 final 類而已。我們常說的主線程就是從這個類的 main 方法開始,main 方法很簡短,一眼就能看全(如上),我們看到裏面有 Looper 了,那麽接下來就找找 ActivityThread 對應的 Handler 啊,就是內部類 H,其繼承 Handler,貼出 handleMessage 的小部分:

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;

            ...........
}

看完這 Handler 裏處理消息的內容應該明白了吧, Activity 的生命周期都有對應的 case 條件了,ActivityThread 有個 getHandler 方法,得到這個 handler 就可以發送消息,然後 loop 裏就分發消息,然後就發給 handler, 然後就執行到 H(Handler )裏的對應代碼。所以這些代碼就不會卡死~,有消息過來就能執行。舉個例子,在 ActivityThread 裏的內部類 ApplicationThread 中就有很多 sendMessage 的方法。
簡單的來說:ActivityThread的main方法主要就是做消息循環,一旦退出消息循環,那麽你的程序也就可以退出了。
從消息隊列中取消息可能會阻塞,取到消息會做出相應的處理。如果某個消息處理時間過長,就可能會影響UI線程的刷新速率,造成卡頓的現象。

如果你了解下linux的epoll你就知道為什麽不會被卡住了,先說結論:阻塞是有的,但是不會卡住
主要原因有2個

  1. epoll模型
    當沒有消息的時候會epoll.wait,等待句柄寫的時候再喚醒,這個時候其實是阻塞的。

  2. 所有的ui操作都通過handler來發消息操作。
    比如屏幕刷新16ms一個消息,你的各種點擊事件,所以就會有句柄寫操作,喚醒上文的wait操作,所以不會被卡死了。

這裏涉及線程,先說說說進程/線程:
進程:每個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時間片。

Android中為什麽主線程不會因為Looper.loop()方法造成阻塞