kyle shanks
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 'AndroidThread - App 進程' disqus: kyleAlien --- AndroidThread - App 進程 === ## OverView of Content 進程(Process)的概念是,程序的運行實例(簡單理解:一個應用就是一個進程) 線程(Thread)是 CPU 調度的基礎單位 這裡還會涉及到 [**Android Handler**](https://hackmd.io/7fBX6uEtQt6AzCpuBWTHMQ?view) 有在另外一篇文章說過 [TOC] ## 概述 一個應用的入口一般來說都是 main 函數(一切的起源),main 函數主要也做了這些事 1. 初始化 比如 Window 環境下就要創建窗口、像系統申請資源 2. **進入死循環** 若在循環中接收到事件則處理,**一直到這個應用進程退出** 一般來說我們會透過 IDE + SDK 開發程序,這相對的會簡單許多,就像是 Android **若是你不了解其中內容你會以為 4 大零組件(Activity、Server、ContentProvider、Broadcast)就是進程** ### AndroidManifest * 從 AndroidManifest.xml 中就可以看到啟動 Activity 的足跡 ```xml= <!-- 四大零組件只是組成 Application 的一部分 --> <application android:name=".application.BwingApplication" android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"> <activity android:name=".ui.activity.boot.BootActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> ``` * **若非特別聲明,所有個組件都運行在當前進程** > Server 就滿常運行在其他進程 ## 觀察 - 進程&線程 一個應用進程一次只會啟動一個線程 ? 主要進程由哪個進程啟動 * 切換到 debug 模式,並且開啟 [**線程監聽欄位**](https://developer.android.com/studio/debug#startdebug) * 使用 `Thread.activeCount()` 也可以看到當前線程的數量 ### 啟動 Acitivty - [ActivityThread](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:fakeandroid/srcs/android/app/ActivityThread.java) * 啟動入口 Activity,並觀察 Threads 列表 ```java= public class BootActivity extends BaseActivity { private static final String TAG = BootActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { // 斷點設定在 super.onCreate super.onCreate(savedInstanceState); } } ``` > ![](https://i.imgur.com/WnW4kDQ.png) * 從上圖 `Thread stack` 可以發現幾件事情 1. [**ActivityThread**](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:fakeandroid/srcs/android/app/ActivityThread.java):除了 MainThread 之外還會啟動其他線程;而 **==MainThread 由 ZygoteInit 啟動== (`com.android.internal.os`),經過一系列的調用後才會到 Activity** ```java= // ActivityThread.java public static void main(String[] args) { ... // 主線程才能調用該函數 Looper.prepareMainLooper(); ... ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); // 主線程對應的 Thread 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. 另外啟動了 4 個 BinderThread 用於與 Binder 通訊 (留到另外一個章節在說) > ![](https://i.imgur.com/POiHFWZ.png) ### 啟動 Server * 創建一個簡單的 Server,並將它啟動,再來觀察線程的狀態,是否也會有許多線程 ```java= public class TimedTaskService extends Service { @Override public void onCreate() { // 斷點設定在 super.onCreate super.onCreate(); } } ``` > ![reference link](https://i.imgur.com/DHa1Q9j.png) * 從上面可以發現 1. **Server 也是由 ==ZygoteInit 啟動==** (`com.android.internal.os`),啟動流程與 Activity 一致,都是由 **Activity Thread 啟動** > ![](https://i.imgur.com/EKjVFDn.png) 2. **Binder 線程並不是 Activity 獨有,Server 也啟動了 4 個 Binder 線程** > ![](https://i.imgur.com/x8gaAgy.png) ### 啟動兩個 Activity * 啟動 另一個 Activity (LobbyActivity) 並且不結束 BootActivity (也就是將 BootActivity 壓入 Activity 棧中) ```java= // 第一個 Activity public class BootActivity extends BaseActivity { private static final String TAG = BootActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(this, LobbyActivity.class); startActivity(intent); } } //---------------------------------------------------------------------- // 第二個 Activity public class LobbyActivity extends BaseActivity { private static final String TAG = LobbyActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { // 斷點設定在 super.onCreate super.onCreate(savedInstanceState); } } ``` > ![](https://i.imgur.com/v1FHWJP.png) * 上圖可以發現幾個現象 1. LobbyActivity 被執行時,**MainThread (ActivityThread) 仍只有一個 (當前應用的 ActivityThread 號碼為 8186)** > ![](https://i.imgur.com/Rv1Xc0N.png) 2. 上一個 BootActivity 暫時退出運行 3. Binder 線程數量相同 > ![](https://i.imgur.com/5pmam03.png) ### 2 個 Activity 相同進程 - 證明 * 證明 BootActivity、LobbyActivity 在同一個進程 ```java= // 第一個 Activity public class BootActivity extends BaseActivity { private static final String TAG = BootActivity.class.getSimpleName(); public static int TEST = 1; // Default = 1 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(this, LobbyActivity.class // 在啟動 LobbyActivity 之前修改 TEST 為 2,若是不同進程,則 LobbyActivity 就不能取到改變後 TEST 的數值 TEST = 2; Log.w("TEST_PROCESS" , "BootActivity TEST value: " + TEST); startActivity(intent); } } // 第二個 Activity public class LobbyActivity extends BaseActivity { private static final String TAG = LobbyActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.w("TEST_PROCESS" , "BootActivity TEST value: " + BootActivity.TEST); } } ``` * 以下可以證明,這兩個 Activity 是運行在同一個進程 1. 在啟動 LobbyActivity 之前修改 TEST 為 2; 若是不同進程,則 LobbyActivity 就會只會拿到初始值,也就是 1 > ![](https://i.imgur.com/pi8be89.png) 2. 從 Logcat 可以輸出查看:PID(Process id)、TID (Thread id) 也是相同的 > Locat 格式:29806(pid)-2986(Thread id),[**參考 stackoverflow**](https://stackoverflow.com/questions/41607956/explanation-about-logcat) :::success * 補充 也可以讓不同 APK 包裡面的組件運行在相同的進程當中,這樣不同組件還可以相互分享資源 ::: ## [ActivityThread](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java) 概述 對於一個應用來說,ActivityThread#main 就是 App 的進入口 :::success * 誰呼叫 ActivityThread#main ? Zygote 服務 fork 出一個新的 App 進程後,就會 **透過反射呼叫到 `ActivityThread #main` 方法**,詳細可以參考 [**Zygote 分析**](https://hackmd.io/Yw1s2x32QoqawvOsW039ZA?view#Zygote-%E9%80%B2%E7%A8%8B) ::: ```java= // ActivityThread.java // final 類 public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { public static void main(String[] args) { ... Looper.prepareMainLooper(); ... // 創建一個 ActivityThread 對象 ActivityThread thread = new ActivityThread(); // @ 之後分析 attach 方法 thread.attach(false, startSeq); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } } ``` ### ActivityThread - 創建 [Looper](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Looper.java) & Handler * MainLooper 與一般的 prepare 差不多,只差在 **ActivityThread 有對 Main Looper 的管理** 1. **主線程的 Looper 不可以退出,並把它存在靜態變量中** 2. 任何線程都可以透過 getMainLooper() 取得 Main Looper 對象 3. Main Looper 也可以隨時取得當前線程的 Looper ```java= // ActivityThread.java public final class ActivityThread { private static Looper sMainLooper; static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static void prepareMainLooper() { // 1. 該 Looper 不允許退出 // @ 追蹤 prepare 方法 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); // myLooper 可以透過 TheadLocal 獲得 Looper 對象 } } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { // 如果 Looper 已經創建過,則拋出錯誤 // 一個 thread 對應一個 Looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } // 2. 取得 Main Looper 對象 public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } // 3. 隨時取得當前線程的 Looper public static @Nullable Looper myLooper() { return sThreadLocal.get(); } } ``` * ActivityThread 的 Handler 是使用內部類 H class,創建處理消息的對象 ```java= // ActivityThread.java public final class ActivityThread { // 建構 Handler final H mH = new H(); class H extends Handler { @Override public void handleMessage(Message msg) { // 為各種消息做不同處理 switch (msg.what) { ... } } } final Handler getHandler() { return mH; } } ``` * 循環處理消息 [**Looper**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Looper.java) 的 `loop` 方法,這邊要注意一下 `loop` 是 **靜態方法,所以只能訪問靜態的數據** :::info Looper 主要的工作就是從 MessageQueue 中取得消息,並分發給相關的負責人(Handler),**若消息隊列為空,它就很可能會進入睡眠,並讓出 CPU 資源** ::: ```java= // Looper.java public static void loop() { // 由於是靜態函數,所以必須透過 myLooper 來獲取 Loop 對象 final Looper me = myLooper(); ... // 每個 Looper 內都有一個 MessageQueue 對象 final MessageQueue queue = me.mQueue; ... // 開始無限倫循 for (;;) { Message msg = queue.next(); // 若沒有下一個訊息則會阻塞(wait) if (msg == null) { // 若當前隊列中沒有 msg 則說明該線程準備要退出了 return; // 跳出無限循環迴圈 } ... try { // target 就是 Handler,到了這裡就開始分發訊息 // 在這裡就是將消息發送給 H class msg.target.dispatchMessage(msg); ... } ... msg.recycleUnchecked(); // 消除處理完畢,回收 } } ``` * Binder IPC 事件就會透過 Handler 將消息傳送到 Main Thread 中 > ![](https://i.imgur.com/6WP186P.png) ### [ActivityThread](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java) - 呼叫 AMS 啟動 Activity 從 ActivtyThread#**attach** 方法開始分析 ```java= // ActivtyThread.java // main 呼叫 attach 方法 private void attach(boolean system, long startSeq) { if (!system) { // 取得 AMS 通訊代理 final IActivityManager mgr = ActivityManager.getService(); try { // 呼叫 AMS#attachApplication 方法 mgr.attachApplication(mAppThread, startSeq); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } ... 省略部份 } else { ... SystemService 進程才會走到這 } ... } ``` * 這裡從 AMS 開始會接觸到許多類,先看類的功能、關係再追蹤程式碼 * 先看看類的簡介 | 類 | 概述 | 說明 | | -------- | -------- | - | | IApplicationThread | 使用者接口 | 作為 App 應用的服務端,讓系統服務回調 | | ActivtyManagerService | 系統服務 | 使用者訪問的入口 | | ActivityTaskManagerInternal | 抽象類 | 定義 ActivityTask 的相關方法 | | LocalService | ActivityTaskManagerService內 部類 | 實際上會調用 ActivityTaskManagerService 服務 | | RootWindowContainer | Window 容器 | | | ActivityTaskManagerService | 系統服務 | Task 管理服務 | | ActivityTaskSupervisor | - | 負責與 `ActivityTaskManagerService` 通訊 | | ClientLifecycleManager | 管理使用者端生命週期 | | | ClientTransaction | AIDL 傳輸類 | 內部存有使用者 `IApplicationThread` 接口 | * AMS 相關類的 UML > ![](https://i.imgur.com/mNlTF3k.png) * ActivtyThread#attach 主要會分為 SystemService 呼叫、一般 App 呼叫(目前是這個情況),一般 App 又會透過 Binder 代理呼叫 [**AMS**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)#`attachApplication` 方法 1. AMS#**attachApplication** 方法:我們主要看 `attachApplicationLocked` 方法,這個方法很長主要有兩個重點 * IApplicationThread#**binderApplication** 方法:IApplicationThread 是由最一開始 App 進程傳入,該方法 **主要目的是將 App 的 ==ApplicationThread 與 ActiivtyThreadServer 綁定==** * [**ActivityTaskManagerInternal**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java)#**attachApplication** 方法:**啟動 App 的 Activity** ```java= // ActivityManagerService.java public ActivityTaskManagerInternal mAtmInternal; @Override public final void attachApplication(IApplicationThread thread, long startSeq) { // 判斷是否有返回的 IApplicationThread if (thread == null) { throw new SecurityException("Invalid application interface"); } synchronized (this) { int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid, // 呼叫者 pid callingUid, // 呼叫者 uid startSeq); Binder.restoreCallingIdentity(origId); } } private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { ProcessRecord app; // 取得該 App 訊息 ... 省略部份 try { ... 省略部份 if (app.getIsolatedEntryPoint() != null) { ... } else if (instr2 != null) { // 與 AMS 綁定 thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, instr2.mUiAutomationConnection, testMode, /*省略部份參數*/ ); } else { // 與 AMS 綁定 thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, /*省略部份參數*/); } ... 省略部份 } /* 省略 catch */ if (normalMode) { try { // @ 追蹤 attachApplication 方法 didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); } /* 省略 catch */ } ... 省略部份 return true; } ``` > ![](https://i.imgur.com/xY8eg4O.png) 2. 分析 ActivityTaskManagerService#attachApplication 方法:ActivityTaskManagerInternal 是一個抽象類,它的實做在 [**ActivityTaskManagerService**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java) :::info 在這裡 ActivityTaskManagerService 的功能就是與 RootWindowContainer 通訊的橋樑 ::: ```java= // ActivityTaskManagerService.java public class ActivityTaskManagerService extends IActivityTaskManager.Stub { RootWindowContainer mRootWindowContainer; final ActivityTaskManagerInternal mInternal; public ActivityTaskManagerService(Context context) { ... mInternal = new LocalService(); } final class LocalService extends ActivityTaskManagerInternal { ... 省略其他方法 @Override public boolean attachApplication(WindowProcessController wpc) throws RemoteException { synchronized (mGlobalLockWithoutBoost) { ... 省略 trace try { // @ 追蹤 attachApplication 方法 return mRootWindowContainer.attachApplication(wpc); } /* 省略 finally */ } } } } ``` 3. [**RootWindowContainer**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java)#attachApplication:取得裝置上目前所有的螢幕,並遍歷 `DisplayContent` 下所有的 rootTask ```java= // RootWindowContainer.java ActivityTaskSupervisor mTaskSupervisor; boolean attachApplication(WindowProcessController app) throws RemoteException { boolean didSomething = false; for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { mTmpRemoteException = null; mTmpBoolean = false; // Set to true if an activity was started. // 取得要顯示的螢幕 DisplayContent final DisplayContent display = getChildAt(displayNdx); // 遍歷該 DisplayContent 中所有的 RootTasks display.forAllRootTasks(rootTask -> { // 錯誤 if (mTmpRemoteException != null) { return; } // 不可建就忽略 if (rootTask.getVisibility(null /* starting */) == TASK_FRAGMENT_VISIBILITY_INVISIBLE) { return; } // 在這裡會回調 // @ 追蹤 startActivityForAttachedApplicationIfNeeded 方法 final PooledFunction c = PooledLambda.obtainFunction( // 重點 RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this, PooledLambda.__(ActivityRecord.class), app, rootTask.topRunningActivity() ); rootTask.forAllActivities(c); c.recycle(); }); if (mTmpRemoteException != null) { throw mTmpRemoteException; } didSomething |= mTmpBoolean; } if (!didSomething) { ensureActivitiesVisible(null, 0, false /* preserve_windows */); } return didSomething; } private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r, WindowProcessController app, ActivityRecord top) { ... 省略部份 try { // @ 追蹤 realStartActivityLocked 方法 if (mTaskSupervisor.realStartActivityLocked(r, app, top == r && r.getTask().canBeResumed(r) /*andResume*/, true /*checkConfig*/)) { mTmpBoolean = true; } } /* 省略 catch */ return false; } ``` > ![](https://i.imgur.com/Dxronik.png) 4. ActivityTaskSupervisor#realStartActivityLocked:真正啟動 Activity,已最終結果來說,AMS 系統服務會透過 App 傳入的 `IApplicationThread` 回掉到 App 端 ```java= // ActivityTaskSupervisor.java final ActivityTaskManagerService mService; boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { // 檢查是否全部都完成 Paused if (!mRootWindowContainer.allPausedActivitiesComplete()) { ... log 訊息 return false; } final Task task = r.getTask(); final Task rootTask = task.getRootTask(); ... try { r.startFreezingScreenLocked(proc, 0); // 啟動較慢的 App 訊息 r.startLaunchTickingLocked(); // 設定 ActivityRecord 的 proc r.setProcess(proc); // Ensure activity is allowed to be resumed after process has set. if (andResume && !r.canResumeByCompat()) { andResume = false; } ... 省略部份 // 如果進程是第一次啟動活動,則將控制器發送到客戶端 // 所以客戶端可以保存從活動中獲取控制器的綁定 事務 服務。 final IActivityClientController activityClientController = proc.hasEverLaunchedActivity() ? null : mService.mActivityClientController; r.launchCount++; r.lastLaunchTime = SystemClock.uptimeMillis(); // 紀錄啟動時間 proc.setLastActivityLaunchTime(r.lastLaunchTime); ...log 訊息 ... 省略部份 try { if (!proc.hasThread()) { throw new RemoteException(); } List<ResultInfo> results = null; List<ReferrerIntent> newIntents = null; if (andResume) { // We don't need to deliver new intents and/or set results if activity is going // to pause immediately after launch. results = r.results; newIntents = r.newIntents; } ...log, event 訊息 if (r.isActivityTypeHome()) { // 如果是 HomeActivity,就替換 Task 的底部 updateHomeProcess(task.getBottomMostActivity().app); } .. 省略部份 // Create activity launch transaction. // 取得覆用的 ClientTransaction final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); final boolean isTransitionForward = r.isTransitionForward(); // 之後到 Client 端會使用到這個 call back clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, /* 省略部分參數*/ ); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(isTransitionForward); } else { lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); // ActivityTaskManagerService == mService // @ 分析 scheduleTransaction 方法 mService.getLifecycleManager().scheduleTransaction(clientTransaction); ... 省略部份 } /* 省略 catch */ } /* 省略 finally */ ... 省略部份 return true; } // ------------------------------------------------------------- // ActivityTaskManagerService.java public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private final ClientLifecycleManager mLifecycleManager; ... ClientLifecycleManager getLifecycleManager() { return mLifecycleManager; } } // ------------------------------------------------------------- // ClientLifecycleManager.java class ClientLifecycleManager { void scheduleTransaction(ClientTransaction transaction) throws RemoteException { final IApplicationThread client = transaction.getClient(); transaction.schedule(); if (!(client instanceof Binder)) { // If client is not an instance of Binder - it's a remote call and at this point it is // safe to recycle the object. All objects used for local calls will be recycled after // the transaction is executed on client in ActivityThread. transaction.recycle(); } } } // ------------------------------------------------------------- // ClientTransaction.java // AIDL 可傳送類 public class ClientTransaction implements Parcelable, ObjectPoolItem { private IApplicationThread mClient; public void schedule() throws RemoteException { // 呼叫到 Client 端 mClient.scheduleTransaction(this); } } ``` > ![](https://i.imgur.com/MaJ5n63.png) ### [ActivityThread](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java) - scheduleTransaction 處理 AMS 資料 到這一步後,代表 AMS 準備啟動 Activity,它會透過最開始對 AMS 傳入的 `IApplicationThread` 來進行回調,並處理 AMS 傳入的 `ClientTransaction` ```java= // ActivityThread.java public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { private class ApplicationThread extends IApplicationThread.Stub { @Override public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { // @ 追蹤 scheduleTransaction ActivityThread.this.scheduleTransaction(transaction); } } } ``` ActivityThread 相關類 UML > ![](https://i.imgur.com/bbshAko.png) * ActivityThread 繼承 ClientTransactionHandler 類:該用來處理 Binder AMS IPC 回傳的 `ClientTransaction` 對象,並定義 Handler 發送訊息的接口 ```java= // ClientTransactionHandler.java public abstract class ClientTransactionHandler { void scheduleTransaction(ClientTransaction transaction) { transaction.preExecute(this); // 傳輸 EXECUTE_TRANSACTION sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); } // 由 ActivityThread 實作 abstract void sendMessage(int what, Object obj); } ``` * 回到 ActivityThread 類,它會實做 ClientTransactionHandler#sendMessage 方法,並 **透過 Handler 將消息帶回到 Main Thread** | Handler's what | obj | | -------- | -------- | | `H.EXECUTE_TRANSACTION` | `ClientTransaction` | ```java= // ActivityThread.java final H mH = new H(); // what => H.EXECUTE_TRANSACTION // obj => ClientTransaction void sendMessage(int what, Object obj) { sendMessage(what, obj, 0, 0, false); } private void sendMessage(int what, Object obj, int arg1) { sendMessage(what, obj, arg1, 0, false); } private void sendMessage(int what, Object obj, int arg1, int arg2) { sendMessage(what, obj, arg1, arg2, false); } private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { ... debug 訊息 Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; // 異步消息 if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); } ``` * 查看 Handler 如何處理 `EXECUTE_TRANSACTION` 訊息: 1. 透過 TransactionExecutor#execute 處理 `ClientTransaction` 對象 ```java= // ActivityThread.java private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this); class H extends Handler { public static final int EXECUTE_TRANSACTION = 159; public void handleMessage(Message msg) { switch (msg.what) { case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; // @ 追蹤 execute 方法 mTransactionExecutor.execute(transaction); if (isSystem()) { transaction.recycle(); } break; } } } ``` 2. 判斷 ClientTransaction 訊息:處理 AMS 傳給 App 的 `ClientTransaction` 物件,並呼叫 AMS 傳入的 ClientTransaction 的 callback :::info * 從這裡可以看出 Destory 的 Activity 是否真的移除(或是說移除的時機),是由 AMS 決定 ::: ```java= // TransactionExecutor.java private ClientTransactionHandler mTransactionHandler; public void execute(ClientTransaction transaction) { ... debug 訊息 final IBinder token = transaction.getActivityToken(); if (token != null) { // 取得已安排被 destory 的 activity final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed = mTransactionHandler.getActivitiesToBeDestroyed(); // 檢查該請求是否是 destory final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token); if (destroyItem != null) { if (transaction.getLifecycleStateRequest() == destroyItem) { // 它將執行將銷毀活動的事務 token, // 因此可以刪除對應的待銷毀記錄。 activitiesToBeDestroyed.remove(token); } if (mTransactionHandler.getActivityClient(token) == null) { ... debug 訊息 return; } } } ... log 訊息 // @ 追蹤 executeCallbacks executeCallbacks(transaction); executeLifecycleState(transaction); mPendingActions.clear(); ... debug 訊息 } ``` 3. 執行 ClientTransaction's Callback:執行 ClientTransaction#**execute** 方法 ```java= // TransactionExecutor.java private ClientTransactionHandler mTransactionHandler; public void executeCallbacks(ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null || callbacks.isEmpty()) { // No callbacks to execute, return early. return; } ...debug 訊息 final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); ... 省略部份 final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); ...省略部份 // 執行 callback item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); ... 省略部份 } } ``` > ![](https://i.imgur.com/Q9CfGXT.png) ### [ActivityThread](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java) - 回調 ClientTransactionItem ActivityThread 會回調 `ClientTransactionItem`,而 `ClientTransactionItem` 是在 `ActivityTaskSupervisor` 創建(跟 App 不同進程) * 回顧 AMS 設定 Call Back 的時機 ```java= // Create activity launch transaction. 取得覆用的 ClientTransaction final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); // 設定 call back 是 LaunchActivityItem ! clientTransaction.addCallback( LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, /* 省略部分參數*/ ); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(isTransitionForward); } else { lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); ``` * 從上面的簡單回顧我們可以知道 app 進程回調的對象是 [**LaunchActivityItem**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java) 類,調用的方法是 ClientTransactionItem#`execute` ```java= // LaunchActivityItem.java public class LaunchActivityItem extends ClientTransactionItem { @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { // 創建 ActivityClientRecord ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, /* 省略部份參數 */); // @ 追蹤 handleLaunchActivity 方法 client.handleLaunchActivity(r, pendingActions, null /* customIntent */); } } ``` > ![](https://i.imgur.com/oEG6Gmm.png) ### performLaunchActivity - 執行 onCreate * ActivityThread 繼承於 ClientTransactionHandler,所以 `handleLaunchActivity` 方法也由 ActivityThread 實做,該**方法的重點是 `performLaunchActivity`** ```java= // ActivityThread.java public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal { @Override public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ... 省略部份 // @ 追蹤 performLaunchActivity 方法 final Activity a = performLaunchActivity(r, customIntent); ... 省略部份 return a; } } ``` * ActivityThread#performLaunchActivity 方法會分為幾個階段,最終會呼叫到 Activity#onCreate 方法 1. 取得 ActivityInfo、ComponentName、PackageInfo ```java= // ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } } ``` 2. 創建 Activity 還有其對應的 ContextImpl 對象 ```java= // ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; ... ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... 省略部份 } /* 省略 catch */ ... } ``` 3. 從 LoadedApk 取得 Application 對象、呼叫 Activity#attach 方法,透過 Instrumentation#callActivityOnCreate 呼叫到 Activity#onCreate 方法 ```java= // ActivityThread.java Instrumentation mInstrumentation; private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; ... try { Application app = r.packageInfo.makeApplication(false, mInstrumentation); ... 省略部份 if (activity != null) { ... 省略部份 // Context 把 Activity 包裝起來 appContext.setOuterContext(activity); // 呼叫 Activity#attach 方法 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, /* 省略部份參數 */); ... 省略部份 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } ... } r.setState(ON_CREATE); } /* 省略 catch */ return activity; } ``` * 最後我們來看 Instrumentation#callActivityOnCreate 是如何呼叫到 Activity#onCreate 方法 ```java= // Instrumentation.java public void callActivityOnCreate(Activity activity, Bundle icicle) { prePerformCreate(activity); // @ 追蹤 performCreate 方法 activity.performCreate(icicle); postPerformCreate(activity); } // ------------------------------------------------------------ // Activity.java final void performCreate(Bundle icicle) { performCreate(icicle, null); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { ... 省略部份 if (persistentState != null) { onCreate(icicle, persistentState); } else { // @ 追蹤 onCreate onCreate(icicle); } ... 省略部份 } @MainThread @CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) { ... 到這一步就進入我們最常見的 onCreate } ``` 加上 Activity、Instrumentation 類,再次整理 UML > ![](https://i.imgur.com/QXI21ML.png) ## Appendix & FAQ :::info ::: ###### tags: `Android Framework` `Java 多線程`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully