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

Android中為什麼主執行緒不會因為Looper.loop()方法造成阻塞

很多人都對Handler的機制有所瞭解,如果不是很熟悉的可以看看我
如果看過原始碼的人都知道,在處理訊息的時候使用了Looper.loop()方法,並且在該方法中進入了一個死迴圈,同時Looper.loop()方法是在主執行緒中呼叫的,那麼為什麼沒有造成阻塞呢?
首先我們需要從Android程式啟動的入口開始來看:
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時間片。

這是前幾天面試的時候被問到的,這裡面的內容也大多是來自知乎的答案,我算是一個整理吧。