扣丁書屋

Android 應用啟動全流程分析(源碼深度剖析 + Systrace 展示)

1 . 前言

從用戶手指點擊桌面上的應用圖標到屏幕上顯示出應用主 Activity 界面而完成應用啟動,快的話往往都不需要一秒鐘,但是這整個過程卻是十分復雜的,其中涉及了 Android 系統的幾乎所有核心知識點。同時應用的啟動速度也絕對是系統的核心用戶體驗指標之一,多少年來,無論是谷歌或是手機系統廠商們還是各個 Android 應用開發者,都在為實現應用打開速度更快一點的目標而不斷努力

但是要想真正做好應用啟動速度優化這件事情,我想是必須要對應用啟動的整個流程有充分的認識和理解的,所以無論作為 Android 系統或應用開發者,都有必要好好的學習和了解一下這個過程的。網上有很多介紹應用啟動流程源碼的文章,但是總感覺大多數都不太全面,很多只是介紹了應用啟動過程中的部分流程,而沒有總體清晰的認識應用啟動過程的總體脈絡與系統架構設計思想

所以本文將結合筆者多年來的工作經歷,結合 Systrace 分析工具,基于最新 Android R AOSP 源碼完整的分析一下這個從用戶手指觸控點擊屏幕應用圖標到應用界面展示到屏幕上的整個應用啟動過程,也是對之前所做所學的一個總結與歸納

2 . 大綱

  • Android 觸控事件處理機制
  • Zygote 進程啟動和應用進程創建流程
  • Handler 消息機制
  • AMS 的 Activity 組件管理
  • 應用 Application 和 Activity 組件創建與初始化
  • 應用 UI 布局與繪制
  • RenderThread 渲染
  • SurfaceFlinger 合成顯示
  • 寫在最后
  • 參考

3 . Input 觸控事件處理流程

3.1 系統機制分析

Android 系統是由事件驅動的,而 input 是最常見的事件之一,用戶的點擊、滑動、長按等操作,都屬于 input 事件驅動,其中的核心就是 InputReader 和 InputDispatcher。InputReader 和 InputDispatcher 是跑在 SystemServer 進程中的兩個 native 循環線程,負責讀取和分發 Input 事件。整個處理過程大致流程如下:

  1. InputReader 負責從 EventHub 里面把 Input 事件讀取出來,然后交給 InputDispatcher 進行事件分發;
  2. InputDispatcher 在拿到 InputReader 獲取的事件之后,對事件進行包裝后,尋找并分發到目標窗口;
  3. InboundQueue 隊列(“iq”)中放著 InputDispatcher 從 InputReader 中拿到的 input 事件;
  4. OutboundQueue(“oq”)隊列里面放的是即將要被派發給各個目標窗口 App 的事件;
  5. WaitQueue 隊列里面記錄的是已經派發給 App(“wq”),但是 App 還在處理沒有返回處理成功的事件;
  6. PendingInputEventQueue 隊列(“aq”)中記錄的是應用需要處理的 Input 事件,這里可以看到 input 事件已經傳遞到了應用進程;
  7. deliverInputEvent 標識 App UI Thread 被 Input 事件喚醒;
  8. InputResponse 標識 Input 事件區域,這里可以看到一個 Input_Down 事件 + 若干個 Input_Move 事件 + 一個 Input_Up 事件的處理階段都被算到了這里;
  9. App 響應處理 Input 事件,內部會在其界面 View 樹中傳遞處理。

用一張圖描述整個過程大致如下:

3.2 結合 Systrace 分析

從桌面點擊應用圖標啟動應用,system_server 的 native 線程 InputReader 首先負責從 EventHub 中利用 linux 的 epolle 機制監聽并從屏幕驅動讀取上報的觸控事件,然后喚醒另外一條 native 線程 InputDispatcher 負責進行進一步事件分發。InputDispatcher 中會先將事件放到 InboundQueue 也就是 “iq” 隊列中,然后尋找具體處理 input 事件的目標應用窗口,并將事件放入對應的目標窗口 OutboundQueue 也就是 “oq” 隊列中等待通過 SocketPair 雙工信道發送到應用目標窗口中。最后當事件發送給具體的應用目標窗口后,會將事件移動到 WaitQueue 也就是 “wq” 中等待目標應用處理事件完成,并開啟倒計時,如果目標應用窗口在 5S 內沒有處理完成此次觸控事件,就會向 system_server 報應用 ANR 異常事件。以上整個過程在 Android 系統源碼中都加有相應的 systrace tag,如下 systrace 截圖所示:

input 事件處理 1

接著上面的流程繼續往下分析:當 input 觸控事件傳遞到桌面應用進程后,Input 事件到來后先通過 enqueueInputEvent 函數放入 “aq” 本地待處理隊列中,并喚醒應用的 UI 線程在 deliverInputEvent 的流程中進行 input 事件的具體分發與處理。具體會先交給在應用界面 Window 創建時的 ViewRootImpl#setView 流程中創建的多個不同類型的 InputUsage 中依次進行處理(比如對輸入法處理邏輯的封裝 ImeInputUsage),整個處理流程是按照責任鏈的設計模式進行。最后會交給 ViewpostImeInputUsage 中具體進行處理,這里面會從 View 布局樹的根節點 DecorView 開始遍歷整個 View 樹上的每一個子 View 或 ViewGroup 界面進行事件的分發、攔截、處理的邏輯。最后觸控事件處理完成后會調用 finishInputEvent 結束應用對觸控事件處理邏輯,這里面會通過 JNI 調用到 native 層 InputConsumer 的 sendFinishedSignal 函數通知 InputDispatcher 事件處理完成,從觸發從 "wq" 隊列中及時移除待處理事件以免報 ANR 異常。

Input 事件處理 2

桌面應用界面 View 中在連續處理一個 ACTION_DOWN 的 TouchEvent 觸控事件和多個 ACTION_MOVE,直到最后出現一個 ACTION_UP 的 TouchEvent 事件后,判斷屬于 onClick 點擊事件,然后透過 ActivityManager Binder 調用 AMS 的 startActivity 服務接口觸發啟動應用的邏輯。從 systrace 上看如下圖所示:

input 事件處理 3

4 . 應用進程的創建與啟動

4.1 Pause 桌面應用

接著上一節繼續往下看,桌面進程收到 input 觸控事件并處理后 binder 調用框架 AMS 的的 startActivity 接口啟動應用,相關簡化代碼如下:

  /*frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java*/
  private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, Task inTask,
                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
        ...
        try {
            ...
            // 添加“startActivityInner”的systrace tag
            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
            // 執行startActivityInner啟動應用的邏輯
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
            ...
        }
        ...
    }

在執行 startActivityInner 啟動應用邏輯中,AMS 中的 Activity 棧管理的邏輯,檢查發現當前處于前臺 Resume 狀態的 Activity 是桌面應用,所以第一步需要通知桌面應用的 Activity 進入 Paused 狀態,相關簡化代碼邏輯如下:

/*frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java*/
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
   ...
   // mResumedActivity不為null,說明當前存在處于resume狀態的Activity且不是新需要啟動的應用
   if (mResumedActivity != null) {
      // 執行startPausingLocked通知桌面應用進入paused狀態
      pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
   }
   ...
}

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming) {
    ...
    ActivityRecord prev = mResumedActivity;
    ...
    if (prev.attachedToProcess()) {
        try {
             ...
             // 相關執行動作封裝事務,binder通知mResumedActivity也就是桌面執行pause動作
             mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                        prev.configChangeFlags, pauseImmediately));
        } catch (Exception e) {
           ...
        }
     }
     ...
}

桌面應用進程這邊執行收到 pause 消息后執行 Activity 的 onPause 生命周期,并在執行完成后,會 binder 調用 AMS 的 activityPaused 接口通知系統執行完 activity 的 pause 動作,相關代碼如下:

  /*frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java*/
  @Override
  public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        ...
        try {
            // binder通知AMS當前應用activity已經執行完pause的流程
            ActivityTaskManager.getService().activityPaused(token);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

AMS 這邊收到應用的 activityPaused 調用后,繼續執行啟動應用的邏輯,判斷需要啟動的應用 Activity 所在的進程不存在,所以接下來需要先 startProcessAsync 創建應用進程,相關簡化代碼如下:

/*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/
 void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        ...
        // 1.如果wpc不為null且hasThread表示應用Activity所屬進程存在,直接realStartActivityLocked啟動Activity
        if (wpc != null && wpc.hasThread()) {
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
           ...
        }
        ...
        // 2.否則,調用AMS的startProcessAsync正式開始創建應用進程 
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
    }

以上過程從 systrace 上看,如下圖所示:

  1. 通知 pause 桌面應用:
  2. 確認桌面 activityPaused 狀態之后,開始創建應用進程:

4.2 創建應用進程

接上一小節的分析可以知道,Android 應用進程的啟動是被動式的,在桌面點擊圖標啟動一個應用的組件如 Activity 時,如果 Activity 所在的進程不存在,就會創建并啟動進程。Android 系統中一般應用進程的創建都是統一由 zygote 進程 fork 創建的,AMS 在需要創建應用進程時,會通過 socket 連接并通知到到 zygote 進程在開機階段就創建好的 socket 服務端,然后由 zygote 進程 fork 創建出應用進程。整體架構如下圖所示:

應用進程創建流程圖

我們接著上節中的分析,繼續從 AMS#startProcessAsync 創建進程函數入手,繼續看一下應用進程創建相關簡化流程代碼

4.2.1 AMS 發送 socket 請求

  /*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/  
   @GuardedBy("this")
    final ProcessRecord startProcessLocked(...) {
        return mProcessList.startProcessLocked(...);
   }

   /*frameworks/base/services/core/java/com/android/server/am/ProcessList.java*/
   private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
            int mountExternal, String seInfo, String requiredAbi, String instructionSet,
            String invokeWith, long startTime) {
        try {
            // 原生標識應用進程創建所加的systrace tag
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            ...
            // 調用Process的start方法創建進程
            startResult = Process.start(...);
            ...
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

    /*frameworks/base/core/java/android/os/Process.java*/
    public static ProcessStartResult start(...) {
        // 調用ZygoteProcess的start函數
        return ZYGOTE_PROCESS.start(...);
    }

    /*frameworks/base/core/java/android/os/ZygoteProcess.java*/
    public final Process.ProcessStartResult start(...){
        try {
            return startViaZygote(...);
        } catch (ZygoteStartFailedEx ex) {
           ...
        }
    }

    private Process.ProcessStartResult startViaZygote(...){
        ArrayList<String> argsForZygote = new ArrayList<String>();
        ...
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }

在 ZygoteProcess#startViaZygote 中,最后創建應用進程的邏輯:

  1. openZygoteSocketIfNeeded 函數中打開本地 socket 客戶端連接到 zygote 進程的 socket 服務端;
  2. zygoteSendArgsAndGetResult 發送 socket 請求參數,帶上了創建的應用進程參數信息;
  3. return 返回的數據結構 ProcessStartResult 中會有新創建的進程的 pid 字段。

從 systrace 上看這個過程如下:

start_proc

4.2.2 Zygote 處理 socket 請求

其實早在系統開機階段,zygote 進程創建時,就會在 ZygoteInit#main 入口函數中創建服務端 socket,并預加載系統資源和框架類(加速應用進程啟動速度),代碼如下:

 /*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
 public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;
         ...
        try {
            ...
            // 1.preload提前加載框架通用類和系統資源到進程,加速進程啟動
            preload(bootTimingsTraceLog);
            ...
            // 2.創建zygote進程的socket server服務端對象
            zygoteServer = new ZygoteServer(isPrimaryZygote);
            ...
            // 3.進入死循環,等待AMS發請求過來
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            ...
        } finally {
            ...
        }
        ...
    }

繼續往下看 ZygoteServer#runSelectLoop 如何監聽并處理 AMS 客戶端的請求:

 /*frameworks/base/core/java/com/android/internal/os/ZygoteServer.java*/
 Runnable runSelectLoop(String abiList) {
     // 進入死循環監聽
     while (true) {
        while (--pollIndex >= 0) {
           if (pollIndex == 0) {
             ...
           } else if (pollIndex < usapPoolEventFDIndex) {
             // Session socket accepted from the Zygote server socket
             // 得到一個請求連接封裝對象ZygoteConnection
             ZygoteConnection connection = peers.get(pollIndex);
             // processCommand函數中處理AMS客戶端請求
             final Runnable command = connection.processCommand(this, multipleForksOK);
           }
        }
     }
 }

 Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
         ...
         // 1.fork創建應用子進程
         pid = Zygote.forkAndSpecialize(...);
         try {
             if (pid == 0) {
                 ...
                 // 2.pid為0,當前處于新創建的子應用進程中,處理請求參數
                 return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
             } else {
                 ...
                 handleParentProc(pid, serverPipeFd);
             }
          } finally {
             ...
          }
 }

  private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        ...
        // 關閉從父進程zygote繼承過來的ZygoteServer服務端地址
        closeSocket();
        ...
        if (parsedArgs.mInvokeWith != null) {
           ...
        } else {
            if (!isZygote) {
                // 繼續進入ZygoteInit#zygoteInit繼續完成子應用進程的相關初始化工作
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {
                ...
            }
        }
    }

以上過程從 systrace 上看如下圖所示:

zygote_fork

4.2.3 應用進程初始化

接上一節中的分析,zygote 進程監聽接收 AMS 的請求,fork 創建子應用進程,然后 pid 為 0 時進入子進程空間,然后在 ZygoteInit#zygoteInit 中完成進程的初始化動作,相關簡化代碼如下:

/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        ...
        // 原生添加名為“ZygoteInit ”的systrace tag以標識進程初始化流程
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        // 1.RuntimeInit#commonInit中設置應用進程默認的java異常處理機制
        RuntimeInit.commonInit();
        // 2.ZygoteInit#nativeZygoteInit函數中JNI調用啟動進程的binder線程池
        ZygoteInit.nativeZygoteInit();
        // 3.RuntimeInit#applicationInit中反射創建ActivityThread對象并調用其“main”入口方法
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
 }

應用進程啟動后,初始化過程中主要依次完成以下幾件事情:

  1. 應用進程默認的 java 異常處理機制(可以實現監聽、攔截應用進程所有的 Java crash 的邏輯);
  2. JNI 調用啟動進程的 binder 線程池(注意應用進程的 binder 線程池資源是自己創建的并非從 zygote 父進程繼承的);
  3. 通過反射創建 ActivityThread 對象并調用其 “main” 入口方法。

我們繼續看 RuntimeInit#applicationInit 簡化的代碼流程:

 /*frameworks/base/core/java/com/android/internal/os/RuntimeInit.java*/
 protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        ...
        // 結束“ZygoteInit ”的systrace tag
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
  }

  protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;
        try {
            // 1.反射加載創建ActivityThread類對象
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            ...
        }
        Method m;
        try {
            // 2.反射調用其main方法
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            ...
        } catch (SecurityException ex) {
            ...
        }
        ...
        // 3.觸發執行以上邏輯
        return new MethodAndArgsCaller(m, argv);
    }

我們繼續往下看 ActivityThread 的 main 函數中又干了什么:

/*frameworks/base/core/java/android/app/ActivityThread.java*/
public static void main(String[] args) {
     // 原生添加的標識進程ActivityThread初始化過程的systrace tag,名為“ActivityThreadMain”
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
     ...
     // 1.創建并啟動主線程的loop消息循環
     Looper.prepareMainLooper();
     ...
     // 2.attachApplication注冊到系統AMS中
     ActivityThread thread = new ActivityThread();
     thread.attach(false, startSeq);
     ...
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     Looper.loop();
     ...
}

private void attach(boolean system, long startSeq) {
    ...
    if (!system) {
       ...
       final IActivityManager mgr = ActivityManager.getService();
       try {
          // 通過binder調用AMS的attachApplication接口將自己注冊到AMS中
          mgr.attachApplication(mAppThread, startSeq);
       } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
       }
    }
}

可以看到進程 ActivityThread#main 函數初始化的主要邏輯是:

  1. 創建并啟動主線程的 loop 消息循環;
  2. 通過 binder 調用 AMS 的 attachApplication 接口將自己 attach 注冊到 AMS 中。

以上初始化過程。從 systrace 上看如下圖所示:

activitythread_main

5 . 應用主線程消息循環機制建立

接上一節的分析,我們知道應用進程創建后會通過反射創建 ActivityThread 對象并執行其 main 函數,進行主線程的初始化工作:

/*frameworks/base/core/java/android/app/ActivityThread.java*/
public static void main(String[] args) {
     ...
     // 1.創建Looper、MessageQueue
     Looper.prepareMainLooper();
     ...
     // 2.啟動loop消息循環,開始準備接收消息
     Looper.loop();
     ...
}

// 3.創建主線程Handler對象
final H mH = new H();

class H extends Handler {
  ...
}

/*frameworks/base/core/java/android/os/Looper.java*/
public static void prepareMainLooper() {
     // 準備主線程的Looper
     prepare(false);
     synchronized (Looper.class) {
          if (sMainLooper != null) {
              throw new IllegalStateException("The main Looper has already been prepared.");
          }
          sMainLooper = myLooper();
     }
}

private static void prepare(boolean quitAllowed) {
      if (sThreadLocal.get() != null) {
          throw new RuntimeException("Only one Looper may be created per thread");
      }
      // 創建主線程的Looper對象,并通過ThreadLocal機制實現與主線程的一對一綁定
      sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
      // 創建MessageQueue消息隊列
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
}

主線程初始化完成后,主線程就有了完整的 Looper、MessageQueue、Handler,此時 ActivityThread 的 Handler 就可以開始處理 Message,包括 Application、Activity、ContentProvider、Service、Broadcast 等組件的生命周期函數,都會以 Message 的形式,在主線程按照順序處理,這就是 App 主線程的初始化和運行原理,部分處理的 Message 如下

/*frameworks/base/core/java/android/app/ActivityThread.java*/
class H extends Handler {
        public static final int BIND_APPLICATION        = 110;
        @UnsupportedAppUsage
        public static final int RECEIVER                = 113;
        @UnsupportedAppUsage
        public static final int CREATE_SERVICE          = 114;
        @UnsupportedAppUsage
        public static final int BIND_SERVICE            = 121;

        public void handleMessage(Message msg) {
            switch (msg.what) {
                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;
                    ...
            }
         }
         ...
}

主線程初始化完成后,主線程就進入阻塞狀態,等待 Message,一旦有 Message 發過來,主線程就會被喚醒,處理 Message,處理完成之后,如果沒有其他的 Message 需要處理,那么主線程就會進入休眠阻塞狀態繼續等待??梢哉f Android 系統的運行是受消息機制驅動的,而整個消息機制是由上面所說的四個關鍵角色相互配合實現的(Handler、Looper、MessageQueue、Message),其運行原理如下圖所示:

Android 消息機制

  1. Handler : Handler 主要是用來處理 Message,應用可以在任何線程創建 Handler,只要在創建的時候指定對應的 Looper 即可,如果不指定,默認是在當前 Thread 對應的 Looper。
  2. Looper : Looper 可以看成是一個循環器,其 loop 方法開啟后,不斷地從 MessageQueue 中獲取 Message,對 Message 進行 Delivery 和 Dispatch,最終發給對應的 Handler 去處理。
  3. MessageQueue:MessageQueue 就是一個 Message 管理器,隊列中是 Message,在沒有 Message 的時候,MessageQueue 借助 Linux 的 ePoll 機制,阻塞休眠等待,直到有 Message 進入隊列將其喚醒。
  4. Message:Message 是傳遞消息的對象,其內部包含了要傳遞的內容,最常用的包括 what、arg、callback 等。

6 . 應用 Application 和 Activity 組件創建與初始化

6.1 Application 的創建與初始化

從前面 4.2.3 小結中的分析我們知道,應用進程啟動初始化執行 ActivityThread#main 函數過程中,在開啟主線程 loop 消息循環之前,會通過 Binder 調用系統核心服務 AMS 的 attachApplication 接口將自己注冊到 AMS 中。下面我們接著這個流程往下看,我們先從 systrace 上看看 AMS 服務的 attachApplication 接口是如何處理應用進程的 attach 注冊請求的:

attachApplication我們繼續來看相關代碼的簡化流程:

/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/
@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
     ...
     if (app.isolatedEntryPoint != null) {
           ...
     } else if (instr2 != null) {
           // 1.通過oneway異步類型的binder調用應用進程ActivityThread#IApplicationThread#bindApplication接口
           thread.bindApplication(...);
     } else {
           thread.bindApplication(...);
     }
     ...
     // See if the top visible activity is waiting to run in this process...
     if (normalMode) {
          try {
            // 2.繼續執行啟動應用Activity的流程
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
          } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
          }
      }
}

/*frameworks/base/core/java/android/app/ActivityThread.java*/
private class ApplicationThread extends IApplicationThread.Stub {
      @Override
      public final void bindApplication(...) {
            ...
            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            ...
            // 向應用進程主線程Handler發送BIND_APPLICATION消息,觸發在應用主線程執行handleBindApplication初始化動作
            sendMessage(H.BIND_APPLICATION, data);
      }
      ...
}

class H extends Handler {
      ...
      public void handleMessage(Message msg) {
           switch (msg.what) {
                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    // 在應用主線程執行handleBindApplication初始化動作
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                    ...
           }
      }
      ...
}

@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    ...
}

從上面的代碼流程可以看出:AMS 服務在執行應用的 attachApplication 注冊請求過程中,會通過 oneway 類型的 binder 調用應用進程 ActivityThread#IApplicationThread 的 bindApplication 接口,而 bindApplication 接口函數實現中又會通過往應用主線程消息隊列 post BIND_APPLICATION 消息觸發執行 handleBindApplication 初始化函數,從 systrace 看如下圖所示:

handleBindApplication

我們繼續結合代碼看看 handleBindApplication 的簡化關鍵流程:

/*frameworks/base/core/java/android/app/ActivityThread.java*/
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    ...
    // 1.創建應用的LoadedApk對象
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    ...
    // 2.創建應用Application的Context、觸發Art虛擬機加載應用APK的Dex文件到內存中,并加載應用APK的Resource資源
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    ...
    // 3.調用LoadedApk的makeApplication函數,實現創建應用的Application對象
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    ...
    // 4.執行應用Application#onCreate生命周期函數
    mInstrumentation.onCreate(data.instrumentationArgs);
    ...
}

在 ActivityThread#**handleBindApplication 初始化過程中在應用主線程中主要完成如下幾件事件 **:

  1. 根據框架傳入的 ApplicationInfo 信息創建應用 APK 對應的 LoadedApk 對象;
  2. 創建應用 Application 的 Context 對象;
  3. 創建類加載器 ClassLoader 對象并觸發 Art 虛擬機執行 OpenDexFilesFromOat 動作加載應用 APK 的 Dex 文件;
  4. 通過 LoadedApk 加載應用 APK 的 Resource 資源;
  5. 調用 LoadedApk 的 makeApplication 函數,創建應用的 Application 對象;
  6. 執行應用 Application#onCreate 生命周期函數(APP 應用開發者能控制的第一行代碼);

下面我們結合代碼重點看看 APK Dex 文件的加載和 Resource 資源的加載流程。

6.1.1 應用 APK 的 Dex 文件加載

/*frameworks/base/core/java/android/app/ContextImpl.java*/
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
    if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
    // 1.創建應用Application的Context對象
    ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
                0, null, opPackageName);
    // 2.觸發加載APK的DEX文件和Resource資源
    context.setResources(packageInfo.getResources());
    context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
    return context;
}

/*frameworks/base/core/java/android/app/LoadedApk.java*/
@UnsupportedAppUsage
public Resources getResources() {
     if (mResources == null) {
         ...
         // 加載APK的Resource資源
         mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                    getClassLoader()/*觸發加載APK的DEX文件*/, null);
      }
      return mResources;
}

@UnsupportedAppUsage
public ClassLoader getClassLoader() {
     synchronized (this) {
         if (mClassLoader == null) {
             createOrUpdateClassLoaderLocked(null /*addedPaths*/);
          }
          return mClassLoader;
     }
}

private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
     ...
     if (mDefaultClassLoader == null) {
          ...
          // 創建默認的mDefaultClassLoader對象,觸發art虛擬機加載dex文件
          mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
                    zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                    libraryPermittedPath, mBaseClassLoader,
                    mApplicationInfo.classLoaderName, sharedLibraries);
          ...
     }
     ...
     if (mClassLoader == null) {
         // 賦值給mClassLoader對象
         mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                    new ApplicationInfo(mApplicationInfo));
     }
}

/*frameworks/base/core/java/android/app/ApplicationLoaders.java*/
ClassLoader getClassLoaderWithSharedLibraries(...) {
    // For normal usage the cache key used is the same as the zip path.
    return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
}

private ClassLoader getClassLoader(String zip, ...) {
        ...
        synchronized (mLoaders) {
            ...
            if (parent == baseParent) {
                ...
                // 1.創建BootClassLoader加載系統框架類,并增加相應的systrace tag
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
                ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                        zip,  librarySearchPath, libraryPermittedPath, parent,
                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                ...
                return classloader;
            }
            // 2.創建PathClassLoader加載應用APK的Dex類,并增加相應的systrace tag
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
            ClassLoader loader = ClassLoaderFactory.createClassLoader(
                    zip, null, parent, classLoaderName, sharedLibraries);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            return loader;
        }
}

/*frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java*/
public static ClassLoader createClassLoader(...) {
        // 通過new的方式創建ClassLoader對象,最終會觸發art虛擬機加載APK的dex文件
        ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
                ? null
                : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
        if (isPathClassLoaderName(classloaderName)) {
            return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
        }
        ...
}

從以上代碼可以看出:在創建 Application 的 Context 對象后會立馬嘗試去加載 APK 的 Resource 資源,而在這之前需要通過 LoadedApk 去創建類加載器 ClassLoader 對象,而這個過程最終就會觸發 Art 虛擬機加載應用 APK 的 dex 文件,從 systrace 上看如下圖所示:

OpenDexFilesFromOat

具體 art 虛擬機加載 dex 文件的流程由于篇幅所限這里就不展開講了,這邊畫了一張流程圖可以參考一下,感興趣的讀者可以對照追一下源碼流程:

Art 虛擬機 Dex 加載流程

6.1.2 應用 APK 的 Resource 資源加載

/*frameworks/base/core/java/android/app/LoadedApk.java*/
@UnsupportedAppUsage
public Resources getResources() {
     if (mResources == null) {
         ...
         // 加載APK的Resource資源
         mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                    getClassLoader()/*觸發加載APK的DEX文件*/, null);
      }
      return mResources;
}

/*frameworks/base/core/java/android/app/ResourcesManager.java*/
public @Nullable Resources getResources(...) {
      try {
          // 原生Resource資源加載的systrace tag
          Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
          ...
          return createResources(activityToken, key, classLoader, assetsSupplier);
      } finally {
          Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
      }
}

private @Nullable Resources createResources(...) {
      synchronized (this) {
            ...
            // 執行創建Resources資源對象
            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
            if (resourcesImpl == null) {
                return null;
            }
            ...
     }
}

private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
            @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
      ...
      impl = createResourcesImpl(key, apkSupplier);
      ...
}

private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
            @Nullable ApkAssetsSupplier apkSupplier) {
        ...
        // 創建AssetManager對象,真正實現的APK文件加載解析動作
        final AssetManager assets = createAssetManager(key, apkSupplier);
        ...
}

private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
            @Nullable ApkAssetsSupplier apkSupplier) {
        ...
        for (int i = 0, n = apkKeys.size(); i < n; i++) {
            final ApkKey apkKey = apkKeys.get(i);
            try {
                // 通過loadApkAssets實現應用APK文件的加載
                builder.addApkAssets(
                        (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
            } catch (IOException e) {
                ...
            }
        }
        ...   
}

private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
        ...
        if (key.overlay) {
            ...
        } else {
            // 通過ApkAssets從APK文件所在的路徑去加載
            apkAssets = ApkAssets.loadFromPath(key.path,
                    key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
        }
        ...
    }

/*frameworks/base/core/java/android/content/res/ApkAssets.java*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
            throws IOException {
        return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
}

private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
            @Nullable AssetsProvider assets) throws IOException {
        ...
        // 通過JNI調用Native層的系統system/lib/libandroidfw.so庫中的相關C函數實現對APK文件壓縮包的解析與加載
        mNativePtr = nativeLoad(format, path, flags, assets);
        ...
}

從以上代碼可以看出:系統對于應用 APK 文件資源的加載過程其實就是創建應用進程中的 Resources 資源對象的過程,其中真正實現 APK 資源文件的 I/O 解析,最終是借助于 AssetManager 中通過 JNI 調用系統 Native 層的相關 C 函數實現。整個過程從 systrace 上看如下圖所示:

getResources

6.2 Activity 的創建與初始化

我們回到 6.1 小結中,看看 AMS 在收到應用進程的 attachApplication 注冊請求后,先通過 oneway 類型的 binder 調用應用及進程的 IApplicationThread#bindApplication 接口,觸發應用進程在主線程執行 handleBindeApplication 初始化操作,然后繼續執行啟動應用 Activity 的操作,下面我們來看看系統是如何啟動創建應用 Activity 的,簡化代碼流程如下:

/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/
@GuardedBy("this")
private boolean attachApplicationLocked(...) {
     ...
     if (app.isolatedEntryPoint != null) {
           ...
     } else if (instr2 != null) {
           // 1.通過oneway異步類型的binder調用應用進程ActivityThread#IApplicationThread#bindApplication接口
           thread.bindApplication(...);
     } else {
           thread.bindApplication(...);
     }
     ...
     // See if the top visible activity is waiting to run in this process...
     if (normalMode) {
          try {
            // 2.繼續執行啟動應用Activity的流程
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
          } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
          }
      }
}

/*frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java*/
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
       synchronized (mGlobalLockWithoutBoost) {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                // 原生標識attachApplication過程的systrace tag
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
            }
            try {
                return mRootWindowContainer.attachApplication(wpc);
            } finally {
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
       }
}

/*frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java*/
boolean attachApplication(WindowProcessController app) throws RemoteException {
       ...
       final PooledFunction c = PooledLambda.obtainFunction(
                // startActivityForAttachedApplicationIfNeeded執行啟動應用Activity流程
                RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
                PooledLambda.__(ActivityRecord.class), app,
                rootTask.topRunningActivity());
       ...
}

private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
            WindowProcessController app, ActivityRecord top) {
        ...
        try {
            // ActivityStackSupervisor的realStartActivityLocked真正實現啟動應用Activity流程
            if (mStackSupervisor.realStartActivityLocked(r, app,
                    top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {
                ...
            }
        } catch (RemoteException e) {
            ..
        }
}

/*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {
         ...
        // 1.先通過LaunchActivityItem封裝Binder通知應用進程執行Launch Activity動作       
         clientTransaction.addCallback(LaunchActivityItem.obtain(...);
         // Set desired final state.
         final ActivityLifecycleItem lifecycleItem;
         if (andResume) {
                // 2.再通過ResumeActivityItem封裝Binder通知應用進程執行Launch Resume動作        
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
         }
         ...
         clientTransaction.setLifecycleStateRequest(lifecycleItem);
         // 執行以上封裝的Binder調用
         mService.getLifecycleManager().scheduleTransaction(clientTransaction);
         ...
}

從以上代碼分析可以看到,框架 system_server 進程最終是通過 ActivityStackSupervisor#realStartActivityLocked 函數中,通過 LaunchActivityItem 和 ResumeActivityItem 兩個類的封裝,依次實現 binder 調用通知應用進程這邊執行 Activity 的 Launch 和 Resume 動作的,我們繼續往下看相關代碼流程:

6.2.1 Activity Create

/*frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java*/
@Override
public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
     // 原生標識Activity Launch的systrace tag
     Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
     ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
     // 調用到ActivityThread的handleLaunchActivity函數在主線程執行應用Activity的Launch創建動作
     client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
     Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

/*frameworks/base/core/java/android/app/ActivityThread.java*/
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
     ...
     final Activity a = performLaunchActivity(r, customIntent);
     ...
}

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        // 1.創建Activity的Context
        ContextImpl appContext = createBaseContextForActivity(r);
        try {
            //2.反射創建Activity對象
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
        } catch (Exception e) {
            ...
        }
        try {
            ...
            if (activity != null) {
                ...
                // 3.執行Activity的attach動作
                activity.attach(...);
                ...
                // 4.執行應用Activity的onCreate生命周期函數,并在setContentView調用中創建DecorView對象
                mInstrumentation.callActivityOnCreate(activity, r.state);
                ...
            }
            ...
        } catch (SuperNotCalledException e) {
            ...
        }
}

/*frameworks/base/core/java/android/app/Activity.java*/
 @UnsupportedAppUsage
 final void attach(...) {
        ...
        // 1.創建表示應用窗口的PhoneWindow對象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ...
        // 2.為PhoneWindow配置WindowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
}

從上面代碼可以看出,應用進程這邊在收到系統 binder 調用后,在主線程中創建 Activiy 的流程主要步驟如下:

  1. 創建 Activity 的 Context;
  2. 通過反射創建 Activity 對象;
  3. 執行 Activity 的 attach 動作,其中會創建應用窗口的 PhoneWindow 對象并設置 WindowManage;
  4. 執行應用 Activity 的 onCreate 生命周期函數,并在 setContentView 中創建窗口的 DecorView 對象;從 systrace 上看整個過程如下圖所示:

ActivityStart

6.2.2 Activity Resume

/*frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java*/
@Override
public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
   // 原生標識Activity Resume的systrace tag
   Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
   client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                "RESUME_ACTIVITY");
   Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

/*frameworks/base/core/java/android/app/ActivityThread.java*/
 @Override
public void handleResumeActivity(...){
    ...
    // 1.執行performResumeActivity流程,執行應用Activity的onResume生命周期函數
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ...
    if (r.window == null && !a.mFinished && willBeVisible) {
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    ...
                    // 2.執行WindowManager#addView動作開啟視圖繪制邏輯
                    wm.addView(decor, l);
                } else {
                  ...
                }
            }
     }
    ...
}

public ActivityClientRecord performResumeActivity(...) {
    ...
    // 執行應用Activity的onResume生命周期函數
    r.activity.performResume(r.startsNotResumed, reason);
    ...
}

/*frameworks/base/core/java/android/view/WindowManagerGlobal.java*/
public void addView(...) {
     // 創建ViewRootImpl對象
     root = new ViewRootImpl(view.getContext(), display);
     ...
     try {
         // 執行ViewRootImpl的setView函數
         root.setView(view, wparams, panelParentView, userId);
     } catch (RuntimeException e) {
         ...
     } 
}

從上面代碼可以看出,應用進程這邊在接收到系統 Binder 調用請求后,在主線程中 Activiy Resume 的流程主要步驟如下:

  1. 執行應用 Activity 的 onResume 生命周期函數;
  2. 執行 WindowManager 的 addView 動作開啟視圖繪制邏輯;
  3. 創建 Activity 的 ViewRootImpl 對象;
  4. 執行 ViewRootImpl 的 setView 函數開啟 UI 界面繪制動作;

從 systrace 上看整個過程如下圖所示:

activityResume

7 . 應用 UI 布局與繪制

接上一節的分析,應用主線程中在執行 Activity 的 Resume 流程的最后,會創建 ViewRootImpl 對象并調用其 setView 函數,從此并開啟了應用界面 UI 布局與繪制的流程。在開始講解這個過程之前,我們先來整理一下前面代碼中講到的這些概念,如 Activity、PhoneWindow、DecorView、ViewRootImpl、WindowManager 它們之間的關系與職責,因為這些核心類基本構成了 Android 系統的 GUI 顯示系統在應用進程側的核心架構,其整體架構如下圖所示:

GUI_APP

  1. Window 是一個抽象類,通過控制 DecorView 提供了一些標準的 UI 方案,比如背景、標題、虛擬按鍵等,而 PhoneWindow 是 Window 的唯一實現類,在 Activity 創建后的 attach 流程中創建,應用啟動顯示的內容裝載到其內部的 mDecor(DecorView);
  2. DecorView 是整個界面布局 View 控件樹的根節點,通過它可以遍歷訪問到整個 View 控件樹上的任意節點;
  3. WindowManager 是一個接口,繼承自 ViewManager 接口,提供了 View 的基本操作方法;WindowManagerImp 實現了 WindowManager 接口,內部通過組合方式持有 WindowManagerGlobal,用來操作 View;WindowManagerGlobal 是一個全局單例,內部可以通過 ViewRootImpl 將 View 添加至窗口中;
  4. ViewRootImpl 是所有 View 的 Parent,用來總體管理 View 的繪制以及與系統 WMS 窗口管理服務的 IPC 交互從而實現窗口的開辟;ViewRootImpl 是應用進程運轉的發動機,可以看到 ViewRootImpl 內部包含 mView(就是 DecorView)、mSurface、Choregrapher,mView 代表整個控件樹,mSurfacce 代表畫布,應用的 UI 渲染會直接放到 mSurface 中,Choregorapher 使得應用請求 vsync 信號,接收信號后開始渲染流程;

我們從 ViewRootImpl 的 setView 流程繼續結合代碼往下看:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
      synchronized (this) {
         if (mView == null) {
             mView = view;
         }
         ...
         // 開啟繪制硬件加速,初始化RenderThread渲染線程運行環境
         enableHardwareAcceleration(attrs);
         ...
         // 1.觸發繪制動作
         requestLayout();
         ...
         inputChannel = new InputChannel();
         ...
         // 2.Binder調用訪問系統窗口管理服務WMS接口,實現addWindow添加注冊應用窗口的操作,并傳入inputChannel用于接收觸控事件
         res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
         ...
         // 3.創建WindowInputEventReceiver對象,實現應用窗口接收觸控事件
         mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());
         ...
         // 4.設置DecorView的mParent為ViewRootImpl
         view.assignParent(this);
         ...
      }
}

從以上代碼可以看出 ViewRootImpl 的 setView 內部關鍵流程如下:

  1. requestLayout () 通過一系列調用觸發界面繪制(measure、layout、draw)動作,下文會詳細展開分析;
  2. 通過 Binder 調用訪問系統窗口管理服務 WMS 的 addWindow 接口,實現添加、注冊應用窗口的操作,并傳入本地創建 inputChannel 對象用于后續接收系統的觸控事件,這一步執行完我們的 View 就可以顯示到屏幕上了。關于 WMS 的內部實現流程也非常復雜,由于篇幅有限本文就不詳細展開分析了。
  3. 創建 WindowInputEventReceiver 對象,封裝實現應用窗口接收系統觸控事件的邏輯;
  4. 執行 view.assignParent (this),設置 DecorView 的 mParent 為 ViewRootImpl。所以,雖然 ViewRootImpl 不是一個 View, 但它是所有 View 的頂層 Parent。

我們順著 ViewRootImpl 的 requestLayout 動作繼續往下看界面繪制的流程代碼:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
         // 檢查當前UI繪制操作是否發生在主線程,如果發生在子線程則會拋出異常
         checkThread();
         mLayoutRequested = true;
         // 觸發繪制操作
         scheduleTraversals();
    }
}

@UnsupportedAppUsage
void scheduleTraversals() {
    if (!mTraversalScheduled) {
         ...
         // 注意此處會往主線程的MessageQueue消息隊列中添加同步欄刪,因為系統繪制消息屬于異步消息,需要更高優先級的處理
         mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
         // 通過Choreographer往主線程消息隊列添加CALLBACK_TRAVERSAL繪制類型的待執行消息,用于觸發后續UI線程真正實現繪制動作
         mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
         ...
     }
}

Choreographer 的引入,主要是配合系統 Vsync 垂直同步機制(Android “黃油計劃” 中引入的機制之一,協調 APP 生成 UI 數據和 SurfaceFlinger 合成圖像,避免 Tearing 畫面撕裂的現象),給上層 App 的渲染提供一個穩定的 Message 處理的時機,也就是 Vsync 到來的時候 ,系統通過對 Vsync 信號周期的調整,來控制每一幀繪制操作的時機。Choreographer 扮演 Android 渲染鏈路中承上啟下的角色:

  1. 承上:負責接收和處理 App 的各種更新消息和回調,等到 Vsync 到來的時候統一處理。比如集中處理 Input (主要是 Input 事件的處理) 、Animation (動畫相關)、Traversal (包括 measure、layout、draw 等操作) ,判斷卡頓掉幀情況,記錄 CallBack 耗時等;
  2. 啟下:負責請求和接收 Vsync 信號。接收 Vsync 事件回調 (通過 FrameDisplayEventReceiver.onVsync),請求 Vsync (FrameDisplayEventReceiver.scheduleVsync) 。

Choreographer 在收到 CALLBACK_TRAVERSAL 類型的繪制任務后,其內部的工作流程如下圖所示:

Choreographer 工作原理

從以上流程圖可以看出:ViewRootImpl 調用 Choreographer 的 postCallback 接口放入待執行的繪制消息后,Choreographer 會先向系統申請 APP 類型的 vsync 信號,然后等待系統 vsync 信號到來后,去回調到 ViewRootImpl 的 doTraversal 函數中執行真正的繪制動作(measure、layout、draw)。這個繪制過程從 systrace 上看如下圖所示:

UI 繪制任務

我們接著 ViewRootImpl 的 doTraversal 函數的簡化代碼流程往下看:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
void doTraversal() {
     if (mTraversalScheduled) {
         mTraversalScheduled = false;
         // 調用removeSyncBarrier及時移除主線程MessageQueue中的Barrier同步欄刪,以避免主線程發生“假死”
         mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
         ...
         // 執行具體的繪制任務
         performTraversals();
         ...
    }
}

private void performTraversals() {
     ...
     // 1.從DecorView根節點出發,遍歷整個View控件樹,完成整個View控件樹的measure測量操作
     windowSizeMayChange |= measureHierarchy(...);
     ...
     if (mFirst...) {
    // 2.第一次執行traversals繪制任務時,Binder調用訪問系統窗口管理服務WMS的relayoutWindow接口,實現WMS計算應用窗口尺寸并向系統surfaceflinger正式申請Surface“畫布”操作
         relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
     }
     ...
     // 3.從DecorView根節點出發,遍歷整個View控件樹,完成整個View控件樹的layout測量操作
     performLayout(lp, mWidth, mHeight);
     ...
     // 4.從DecorView根節點出發,遍歷整個View控件樹,完成整個View控件樹的draw測量操作
     performDraw();
     ...
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        ...
        // 通過Binder IPC訪問系統WMS服務的relayout接口,申請Surface“畫布”操作
        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize, mBlastSurfaceControl);
        if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
                // 本地Surface對象獲取指向遠端分配的Surface的引用
                mSurface.copyFrom(mSurfaceControl);
            } else {
               ...
            }
        }
        ...
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        ...
        // 原生標識View樹的measure測量過程的trace tag
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            // 從mView指向的View控件樹的根節點DecorView出發,遍歷訪問整個View樹,并完成整個布局View樹的測量工作
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
}

private void performDraw() {
     ...
     boolean canUseAsync = draw(fullRedrawNeeded);
     ...
}

private boolean draw(boolean fullRedrawNeeded) {
    ...
    if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
        ...
        // 如果開啟并支持硬件繪制加速,則走硬件繪制的流程(從Android 4.+開始,默認情況下都是支持跟開啟了硬件加速的)
        mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
    } else {
        // 否則走drawSoftware軟件繪制的流程
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
                    return false;
         }
    }
}

從上面的代碼流程可以看出,ViewRootImpl 中負責的整個應用界面繪制的主要流程如下:

  1. 從界面 View 控件樹的根節點 DecorView 出發,遞歸遍歷整個 View 控件樹,完成對整個 View 控件樹的 measure 測量操作,由于篇幅所限,本文就不展開分析這塊的詳細流程;
  2. 界面第一次執行繪制任務時,會通過 Binder IPC 訪問系統窗口管理服務 WMS 的 relayout 接口,實現窗口尺寸的計算并向系統申請用于本地繪制渲染的 Surface “畫布” 的操作(具體由 SurfaceFlinger 負責創建應用界面對應的 BufferQueueLayer 對象,并通過內存共享的方式通過 Binder 將地址引用透過 WMS 回傳給應用進程這邊),由于篇幅所限,本文就不展開分析這塊的詳細流程;
  3. 從界面 View 控件樹的根節點 DecorView 出發,遞歸遍歷整個 View 控件樹,完成對整個 View 控件樹的 layout 測量操作;
  4. 從界面 View 控件樹的根節點 DecorView 出發,遞歸遍歷整個 View 控件樹,完成對整個 View 控件樹的 draw 測量操作,如果開啟并支持硬件繪制加速(從 Android 4.X 開始谷歌已經默認開啟硬件加速),則走 GPU 硬件繪制的流程,否則走 CPU 軟件繪制的流程;

以上繪制過程從 systrace 上看如下圖所示:

performTraversal 繪制

relayoutWindow

借用一張圖來總結應用 UI 繪制的流程,如下所示:

UI 繪制流程

8 . RenderThread 渲染

截止到目前,在 ViewRootImpl 中完成了對界面的 measure、layout 和 draw 等繪制流程后,用戶依然還是看不到屏幕上顯示的應用界面內容,因為整個 Android 系統的顯示流程除了前面講到的 UI 線程的繪制外,界面還需要經過 RenderThread 線程的渲染處理,渲染完成后,還需要通過 Binder 調用 “上幀” 交給 surfaceflinger 進程中進行合成后送顯才能最終顯示到屏幕上。本小節中,我們將接上一節中 ViewRootImpl 中最后 draw 的流程繼續往下分析開啟硬件加速情況下,RenderThread 渲染線程的工作流程。由于目前 Android 4.X 之后系統默認界面是開啟硬件加速的,所以本文我們重點分析硬件加速條件下的界面渲染流程,我們先分析一下簡化的代碼流程:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
private boolean draw(boolean fullRedrawNeeded) {
    ...
    if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
        ...
        // 硬件加速條件下的界面渲染流程
        mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
    } else {
        ...
    }
}

/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
    ...
    // 1.從DecorView根節點出發,遞歸遍歷View控件樹,記錄每個View節點的繪制操作命令,完成繪制操作命令樹的構建
    updateRootDisplayList(view, callbacks);
    ...
    // 2.JNI調用同步Java層構建的繪制命令樹到Native層的RenderThread渲染線程,并喚醒渲染線程利用OpenGL執行渲染任務;
    int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
    ...
}

從上面的代碼可以看出,硬件加速繪制主要包括兩個階段:

  1. 從 DecorView 根節點出發,遞歸遍歷 View 控件樹,記錄每個 View 節點的 drawOp 繪制操作命令,完成繪制操作命令樹的構建;
  2. JNI 調用同步 Java 層構建的繪制命令樹到 Native 層的 RenderThread 渲染線程,并喚醒渲染線程利用 OpenGL 執行渲染任務;

8.1 構建繪制命令樹

我們先來看看第一階段構建繪制命令樹的代碼簡化流程:

/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
        // 原生標記構建View繪制操作命令樹過程的systrace tag
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
        // 遞歸子View的updateDisplayListIfDirty實現構建DisplayListOp
        updateViewTreeDisplayList(view);
        ...
        if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
            // 獲取根View的SkiaRecordingCanvas
            RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
            try {
                ...
                // 利用canvas緩存DisplayListOp繪制命令
                canvas.drawRenderNode(view.updateDisplayListIfDirty());
                ...
            } finally {
                // 將所有DisplayListOp繪制命令填充到RootRenderNode中
                mRootNode.endRecording();
            }
        }
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

private void updateViewTreeDisplayList(View view) {
        ...
        // 從DecorView根節點出發,開始遞歸調用每個View樹節點的updateDisplayListIfDirty函數
        view.updateDisplayListIfDirty();
        ...
}

/*frameworks/base/core/java/android/view/View.java*/
public RenderNode updateDisplayListIfDirty() {
     ...
     // 1.利用`View`對象構造時創建的`RenderNode`獲取一個`SkiaRecordingCanvas`“畫布”;
     final RecordingCanvas canvas = renderNode.beginRecording(width, height);
     try {
         ...
         if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
              // 如果僅僅是ViewGroup,并且自身不用繪制,直接遞歸子View
              dispatchDraw(canvas);
              ...
         } else {
              // 2.利用SkiaRecordingCanvas,在每個子View控件的onDraw繪制函數中調用drawLine、drawRect等繪制操作時,創建對應的DisplayListOp繪制命令,并緩存記錄到其內部的SkiaDisplayList持有的DisplayListData中;
              draw(canvas);
         }
     } finally {
         // 3.將包含有`DisplayListOp`繪制命令緩存的`SkiaDisplayList`對象設置填充到`RenderNode`中;
         renderNode.endRecording();
         ...
     }
     ...
}

public void draw(Canvas canvas) {
    ...
    // draw the content(View自己實現的onDraw繪制,由應用開發者自己實現)
    onDraw(canvas);
    ...
    // draw the children
    dispatchDraw(canvas);
    ...
}

/*frameworks/base/graphics/java/android/graphics/RenderNode.java*/
public void endRecording() {
        ...
        // 從SkiaRecordingCanvas中獲取SkiaDisplayList對象
        long displayList = canvas.finishRecording();
        // 將SkiaDisplayList對象填充到RenderNode中
        nSetDisplayList(mNativeRenderNode, displayList);
        canvas.recycle();
}

從以上代碼可以看出,構建繪制命令樹的過程是從 View 控件樹的根節點 DecorView 觸發,遞歸調用每個子 View 節點的 updateDisplayListIfDirty 函數,最終完成繪制樹的創建,簡述流程如下:

  1. 利用 View 對象構造時創建的 RenderNode 獲取一個 SkiaRecordingCanvas “畫布”;
  2. 利用 SkiaRecordingCanvas,在每個子 View 控件的 onDraw 繪制函數中調用 drawLine、drawRect 等繪制操作時,創建對應的 DisplayListOp 繪制命令,并緩存記錄到其內部的 SkiaDisplayList 持有的 DisplayListData 中;
  3. 將包含有 DisplayListOp 繪制命令緩存的 SkiaDisplayList 對象設置填充到 RenderNode 中;
  4. 最后將根 View 的緩存 DisplayListOp 設置到 RootRenderNode 中,完成構建。

以上整個構建繪制命令樹的過程可以用如下流程圖表示:

硬件加速繪制之繪制命令樹構建

硬件加速下的整個界面的 View 樹的結構如下圖所示:

硬件繪制下的 View 樹結構

最后從 systrace 上看這個過程如下圖所示:

構建 View 繪制命令樹

8.2 執行渲染繪制任務

經過上一小節中的分析,應用在 UI 線程中從根節點 DecorView 出發,遞歸遍歷每個子 View 節點,搜集其 drawXXX 繪制動作并轉換成 DisplayListOp 命令,將其記錄到 DisplayListData 并填充到 RenderNode 中,最終完成整個 View 繪制命令樹的構建。從此 UI 線程的繪制任務就完成了。下一步 UI 線程將喚醒 RenderThread 渲染線程,觸發其利用 OpenGL 執行界面的渲染任務,本小節中我們將重點分析這個流程。我們還是先看看這塊代碼的簡化流程:

/*frameworks/base/graphics/java/android/graphics/HardwareRenderer.java*/
public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) {
    // JNI調用native層的相關函數
    return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length);
}

/*frameworks/base/libs/hwui/jni/android_graphics_HardwareRenderer.cpp*/
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
    ...
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
    return proxy->syncAndDrawFrame();
}

/*frameworks/base/libs/hwui/renderthread/RenderProxy.cpp*/
int RenderProxy::syncAndDrawFrame() {
    // 喚醒RenderThread渲染線程,執行DrawFrame繪制任務
    return mDrawFrameTask.drawFrame();
}

/*frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp*/
int DrawFrameTask::drawFrame() {
    ...
    postAndWait();
    ...
}

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    // 向RenderThread渲染線程的MessageQueue消息隊列放入一個待執行任務,以將其喚醒執行run函數
    mRenderThread->queue().post([this]() { run(); });
    // UI線程暫時進入wait等待狀態
    mSignal.wait(mLock);
}

void DrawFrameTask::run() {
    // 原生標識一幀渲染繪制任務的systrace tag
    ATRACE_NAME("DrawFrame");
    ...
    {
        TreeInfo info(TreeInfo::MODE_FULL, *mContext);
        //1.將UI線程構建的DisplayListOp繪制命令樹同步到RenderThread渲染線程
        canUnblockUiThread = syncFrameState(info);
        ...
    }
    ...
    // 同步完成后則可以喚醒UI線程
    if (canUnblockUiThread) {
        unblockUiThread();
    }
    ...
    if (CC_LIKELY(canDrawThisFrame)) {
        // 2.執行draw渲染繪制動作
        context->draw();
    } else {
        ...
    }
    ...
}

bool DrawFrameTask::syncFrameState(TreeInfo& info) {
    ATRACE_CALL();
    ...
    // 調用CanvasContext的prepareTree函數實現繪制命令樹同步的流程
    mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
    ...
}

/*frameworks/base/libs/hwui/renderthread/CanvasContext.cpp*/
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued,
                                RenderNode* target) {
     ...
     for (const sp<RenderNode>& node : mRenderNodes) {
        ...
        // 遞歸調用各個子View對應的RenderNode執行prepareTree動作
        node->prepareTree(info);
        ...
    }
    ...
}

/*frameworks/base/libs/hwui/RenderNode.cpp*/
void RenderNode::prepareTree(TreeInfo& info) {
    ATRACE_CALL();
    ...
    prepareTreeImpl(observer, info, false);
    ...
}

void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
    ...
    if (info.mode == TreeInfo::MODE_FULL) {
        // 同步繪制命令樹
        pushStagingDisplayListChanges(observer, info);
    }
    if (mDisplayList) {
        // 遍歷調用各個子View對應的RenderNode的prepareTreeImpl
        bool isDirty = mDisplayList->prepareListAndChildren(
                observer, info, childFunctorsNeedLayer,
                [](RenderNode* child, TreeObserver& observer, TreeInfo& info,
                   bool functorsNeedLayer) {
                    child->prepareTreeImpl(observer, info, functorsNeedLayer);
                });
        ...
    }
    ...
}

void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
    ...
    syncDisplayList(observer, &info);
    ...
}

void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
    ...
    // 完成賦值同步DisplayList對象
    mDisplayList = mStagingDisplayList;
    mStagingDisplayList = nullptr;
    ...
}

void CanvasContext::draw() {
    ...
    // 1.調用OpenGL庫使用GPU,按照構建好的繪制命令完成界面的渲染
    bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                                      mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                      &(profiler()));
    ...
    // 2.將前面已經繪制渲染好的圖形緩沖區Binder上幀給SurfaceFlinger合成和顯示
    bool didSwap =
            mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
    ...
}

從以上代碼可以看出:UI 線程利用 RenderProxy 向 RenderThread 線程發送一個 DrawFrameTask 任務請求,RenderThread 被喚醒,開始渲染,大致流程如下:

  1. syncFrameState 中遍歷 View 樹上每一個 RenderNode,執行 prepareTreeImpl 函數,實現同步繪制命令樹的操作;
  2. 調用 OpenGL 庫 API 使用 GPU,按照構建好的繪制命令完成界面的渲染(具體過程,由于本文篇幅所限,暫不展開分析);
  3. 將前面已經繪制渲染好的圖形緩沖區 Binder 上幀給 SurfaceFlinger 合成和顯示;

整個過程可以用如下流程圖表示

RenderThread 線程渲染流程

從 systrace 上這個過程如下圖所示:

RenderThread 實現界面渲染

9 . SurfaceFlinger 合成顯示

SurfaceFlinger 合成顯示部分完全屬于 Android 系統 GUI 中圖形顯示的內容,邏輯結構也比較復雜,但不屬于本文介紹內容的重點。所以本小節中只是總體上介紹一下其工作原理與思想,不再詳細分析源碼,感興趣的讀者可以關注筆者后續的文章再來詳細分析講解。簡單的說 SurfaceFlinger 作為系統中獨立運行的一個 Native 進程,借用 Android 官網的描述,其職責就是負責接受來自多個來源的數據緩沖區,對它們進行合成,然后發送到顯示設備。如下圖所示:

SurfaceFlinger 工作原理

從上圖可以看出,其實 SurfaceFlinger 在 Android 系統的整個圖形顯示系統中是起到一個承上啟下的作用:

  1. 對上:通過 Surface 與不同的應用進程建立聯系,接收它們寫入 Surface 中的繪制緩沖數據,對它們進行統一合成。
  2. 對下:通過屏幕的后緩存區與屏幕建立聯系,發送合成好的數據到屏幕顯示設備。

圖形的傳遞是通過 Buffer 作為載體,Surface 是對 Buffer 的進一步封裝,也就是說 Surface 內部具有多個 Buffer 供上層使用,如何管理這些 Buffer 呢?答案就是 BufferQueue ,下面我們來看看 BufferQueue 的工作原理:

9.1 BufferQueue 機制

借用一張經典的圖來描述 BufferQueue 的工作原理:

BufferQueue 狀態轉換圖

BufferQueue 是一個典型的生產者 - 消費者模型中的數據結構。在 Android 應用的渲染流程中,應用扮演的就是 “生產者” 的角色,而 SurfaceFlinger 扮演的則是 “消費者” 的角色,其配合工作的流程如下:

  1. 應用進程中在開始界面的繪制渲染之前,需要通過 Binder 調用 dequeueBuffer 接口從 SurfaceFlinger 進程中管理的 BufferQueue 中申請一張處于 free 狀態的可用 Buffer,如果此時沒有可用 Buffer 則阻塞等待;
  2. 應用進程中拿到這張可用的 Buffer 之后,選擇使用 CPU 軟件繪制渲染或 GPU 硬件加速繪制渲染,渲染完成后再通過 Binder 調用 queueBuffer 接口將緩存數據返回給應用進程對應的 BufferQueue(如果是 GPU 渲染的話,這里還有個 GPU 處理的過程,所以這個 Buffer 不會馬上可用,需要等 GPU 渲染完成的 Fence 信號),并申請 sf 類型的 Vsync 以便喚醒 “消費者” SurfaceFlinger 進行消費;
  3. SurfaceFlinger 在收到 Vsync 信號之后,開始準備合成,使用 acquireBuffer 獲取應用對應的 BufferQueue 中的 Buffer 并進行合成操作;
  4. 合成結束后,SurfaceFlinger 將通過調用 releaseBuffer 將 Buffer 置為可用的 free 狀態,返回到應用對應的 BufferQueue 中。

9.2 Vsync 同步機制

Vysnc 垂直同步是 Android 在 “黃油計劃” 中引入的一個重要機制,本質上是為了協調 BufferQueue 的應用生產者生成 UI 數據動作和 SurfaceFlinger 消費者的合成消費動作,避免出現畫面撕裂的 Tearing 現象。Vysnc 信號分為兩種類型:

  1. app 類型的 Vsync:app 類型的 Vysnc 信號由上層應用中的 Choreographer 根據繪制需求進行注冊和接收,用于控制應用 UI 繪制上幀的生產節奏。根據第 7 小結中的分析:應用在 UI 線程中調用 invalidate 刷新界面繪制時,需要先透過 Choreographer 向系統申請注冊 app 類型的 Vsync 信號,待 Vsync 信號到來后,才能往主線程的消息隊列放入待繪制任務進行真正 UI 的繪制動作;
  2. sf 類型的 Vsync: sf 類型的 Vsync 是用于控制 SurfaceFlinger 的合成消費節奏。應用完成界面的繪制渲染后,通過 Binder 調用 queueBuffer 接口將緩存數據返還給應用對應的 BufferQueue 時,會申請 sf 類型的 Vsync,待 SurfaceFlinger 在其 UI 線程中收到 Vsync 信號之后,便開始進行界面的合成操作。

Vsync 信號的生成是參考屏幕硬件的刷新周期的,其架構如下圖所示:

Vsync

本小節所描述的流程,從 systrace 上看 SurfaceFlinger 處理應用上幀工作的流程如下圖所示:

requestVsync

SurfaceFlinger 處理

10 . 寫在最后

至此,本文結合源碼和 systrace 完整的分析了從用戶手指點擊桌面上的應用圖標到屏幕上顯示出應用主 Activity 界面第一幀畫面的完整流程,這其中涉及了 App 應用、system_server 框架、Art 虛擬機、surfaceflinger 等一系列 Android 系統核心模塊的相互配合,有很多的細節也由于篇幅所限無法完全展開分析,感興趣的讀者可以結合 AOSP 源碼繼續深入分析。而優化應用啟動打開的速度這個系統核心用戶體驗的指標,也是多少年來谷歌、SOC 芯片廠商、ODM 手機廠商以及各個應用開發者共同努力優化的方向:

  1. 對于 SOC 芯片廠商而言:需要不斷升級 CPU 和 GPU 的硬件算力;
  2. 對于 Android 系統的維護者谷歌而言:在 Android 系統大版本升級過程中,不斷的優化應用啟動過程上的各個系統流程,比如進程創建的速度優化、Art 虛擬機的引入與性能優化、View 繪制流程的簡化、硬件繪制加速機制的引入、系統核心 AMS、WMS 等核心服務的鎖優化等;
  3. 對于各個 ODM 手機廠商而言:會開發識別應用啟動的場景,進行針對性的 CPU 主頻的拉升調節、觸控響應速度的優化等機制;
  4. 對于各個應用開發者而言:會結合自己的業務對應用啟動的場景進行優化,比如盡量減少或推遲在 Application、Activity 生命周期函數中的初始化邏輯、去除界面布局的過度繪制、異步化的布局 XML 文件解析等機制。

本文只是分析了應用啟動一般性流程,至于如何去優化應用啟動的速度,可以關注筆者后續文章的更新,而本文則可以作為應用啟動優化課題的一個基礎認知。最后用一張流程圖來概述一下應用啟動流程的全貌:

應用冷啟動流程

11 . 參考

1 . Systrace 流暢性實戰 1 :了解卡頓原理 https://www.androidperformance.com/2021/04/24/android-systrace-smooth-in-action-1/

2 . 史上最全 Android 渲染機制講解(長文源碼深度剖析)https://mp.weixin.qq.com/s?__biz=MzU2MTk0ODUxOQ==&mid=2247483782&idx=1&sn=f9eae167b217c83036b3a24cd4182cd1&chksm=fc71b38ecb063a9847f4518802fc541091d7f708b112399ec39827e68a6f590249748d643747&mpshare=1&scene=1&srcid=0224RGsfWeG5GyMpxLwEhx7N&sharer_sharetime=1582507745901&sharer_shareid=2d76fc4769fc55b6ca84ec3820ba5821#rd

3 . 理解 Android 硬件加速原理的小白文 https://www.jianshu.com/p/40f660e17a73


https://mp.weixin.qq.com/s/8WR6YfnJrQmRWTbY4s0b7Q

最多閱讀

簡化Android的UI開發 2年以前  |  515113次閱讀
Android 深色模式適配原理分析 1年以前  |  26416次閱讀
Android 樣式系統 | 主題背景覆蓋 1年以前  |  7953次閱讀
Android Studio 生成so文件 及調用 1年以前  |  5587次閱讀
30分鐘搭建一個android的私有Maven倉庫 3年以前  |  4751次閱讀
Android設計與開發工作流 2年以前  |  4413次閱讀
Google Enjarify:可代替dex2jar的dex反編譯 3年以前  |  4397次閱讀
Android多渠道打包工具:apptools 3年以前  |  4028次閱讀
移動端常見崩潰指標 2年以前  |  4014次閱讀
Google Java編程風格規范(中文版) 3年以前  |  3942次閱讀
Android-模塊化-面向接口編程 1年以前  |  3857次閱讀
Android內存異常機制(用戶空間)_NE 1年以前  |  3824次閱讀
Android UI基本技術點 3年以前  |  3790次閱讀
Android死鎖初探 2年以前  |  3734次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>