【凱子哥帶你學Framework】Activity啟動過程全解析
It’s right time to learn Android’s Framework !
前言
- 一個App是怎麼啟動起來的?
- App的程式入口到底是哪裡?
- Launcher到底是什麼神奇的東西?
- 聽說還有個AMS的東西,它是做什麼的?
- Binder是什麼?他是如何進行IPC通訊的?
- Activity生命週期到底是什麼時候呼叫的?被誰呼叫的?
- 等等…
你是不是還有很多類似的疑問一直沒有解決?沒關係,這篇文章將結合原始碼以及大量的優秀文章,站在巨人的肩膀上,更加通俗的來試著解釋一些問題。但是畢竟原始碼繁多、經驗有限,文中不免會出現一些紕漏甚至是錯誤,還懇請大家指出,互相學習。
學習目標
- 瞭解從手機開機第一個zygote程序建立,到點選桌面上的圖示,進入一個App的完整流程,並且從原始碼的角度瞭解到一個Activity的生命週期是怎麼回事
- 瞭解到ActivityManagerServices(即AMS)、ActivityStack、ActivityThread、Instrumentation等Android framework中非常重要的基礎類的作用,及相互間的關係
- 瞭解AMS與ActivityThread之間利用Binder進行IPC通訊的過程,瞭解AMS和ActivityThread在控制Activity生命週期起到的作用和相互之間的配合
- 瞭解與Activity相關的framework層的其他瑣碎問題
寫作方式
這篇文章我決定採用一問一答的方式進行。
其實在這之前,我試過把每個流程的程式碼呼叫過程,用貼上原始碼的方式寫在文章裡,但是寫完一部分之後,發現由於程式碼量太大,整篇文章和老太太的裹腳布一樣——又臭又長,雖然每個重要的操作可以顯示出詳細呼叫過程,但是太關注於細節反而導致從整體上不能很好的把握。所以在原來的基礎之上進行了修改,對關鍵的幾個步驟進行重點介紹,力求語言簡潔,重點突出,從而讓大家在更高的層次上對framework層有個認識,然後結合後面我給出的參考資料,大家就可以更加快速,更加高效的瞭解這一塊的整體架構。
主要物件功能介紹
我們下面的文章將圍繞著這幾個類進行介紹。可能你第一次看的時候,印象不深,不過沒關係,當你跟隨者我讀完這篇文章的時候,我會在最後再次列出這些物件的功能,相信那時候你會對這些類更加的熟悉和深刻。
- ActivityManagerServices,簡稱AMS,服務端物件,負責系統中所有Activity的生命週期
- ActivityThread,App的真正入口。當開啟App之後,會呼叫main()開始執行,開啟訊息迴圈佇列,這就是傳說中的UI執行緒或者叫主執行緒。與ActivityManagerServices配合,一起完成Activity的管理工作
- ApplicationThread,用來實現ActivityManagerService與ActivityThread之間的互動。在ActivityManagerService需要管理相關Application中的Activity的生命週期時,通過ApplicationThread的代理物件與ActivityThread通訊。
- ApplicationThreadProxy,是ApplicationThread在伺服器端的代理,負責和客戶端的ApplicationThread通訊。AMS就是通過該代理與ActivityThread進行通訊的。
- Instrumentation,每一個應用程式只有一個Instrumentation物件,每個Activity內都有一個對該物件的引用。Instrumentation可以理解為應用程序的管家,ActivityThread要建立或暫停某個Activity時,都需要通過Instrumentation來進行具體的操作。
- ActivityStack,Activity在AMS的棧管理,用來記錄已經啟動的Activity的先後關係,狀態資訊等。通過ActivityStack決定是否需要啟動新的程序。
- ActivityRecord,ActivityStack的管理物件,每個Activity在AMS對應一個ActivityRecord,來記錄Activity的狀態以及其他的管理資訊。其實就是伺服器端的Activity物件的映像。
- TaskRecord,AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity啟動和退出的順序。如果你清楚Activity的4種launchMode,那麼對這個概念應該不陌生。
主要流程介紹
下面將按照App啟動過程的先後順序,一問一答,來解釋一些事情。
讓我們開始吧!
zygote是什麼?有什麼作用?
首先,你覺得這個單詞眼熟不?當你的程式Crash的時候,列印的紅色log下面通常帶有這一個單詞。
zygote意為“受精卵“。Android是基於Linux系統的,而在Linux中,所有的程序都是由init程序直接或者是間接fork出來的,zygote程序也不例外。
在Android系統裡面,zygote是一個程序的名字。Android是基於Linux System的,當你的手機開機的時候,Linux的核心載入完成之後就會啟動一個叫“init“的程序。在Linux System裡面,所有的程序都是由init程序fork出來的,我們的zygote程序也不例外。
我們都知道,每一個App其實都是
- 一個單獨的dalvik虛擬機器
- 一個單獨的程序
所以當系統裡面的第一個zygote程序執行之後,在這之後再開啟App,就相當於開啟一個新的程序。而為了實現資源共用和更快的啟動速度,Android系統開啟新程序的方式,是通過fork第一個zygote程序實現的。所以說,除了第一個zygote程序,其他應用所在的程序都是zygote的子程序,這下你明白為什麼這個程序叫“受精卵”了吧?因為就像是一個受精卵一樣,它能快速的分裂,並且產生遺傳物質一樣的細胞!
SystemServer是什麼?有什麼作用?它與zygote的關係是什麼?
首先我要告訴你的是,SystemServer也是一個程序,而且是由zygote程序fork出來的。
知道了SystemServer的本質,我們對它就不算太陌生了,這個程序是Android Framework裡面兩大非常重要的程序之一——另外一個程序就是上面的zygote程序。
為什麼說SystemServer非常重要呢?因為系統裡面重要的服務都是在這個程序裡面開啟的,比如
ActivityManagerService、PackageManagerService、WindowManagerService等等,看著是不是都挺眼熟的?
那麼這些系統服務是怎麼開啟起來的呢?
在zygote開啟的時候,會呼叫ZygoteInit.main()進行初始化
public static void main(String argv[]) {
...ignore some code...
//在載入首個zygote的時候,會傳入初始化引數,使得startSystemServer = true
boolean startSystemServer = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
...ignore some code...
//開始fork我們的SystemServer程序
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
...ignore some code...
}
我們看下startSystemServer()做了些什麼
/**留著這個註釋,就是為了說明SystemServer確實是被fork出來的
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
...ignore some code...
//留著這段註釋,就是為了說明上面ZygoteInit.main(String argv[])裡面的argv就是通過這種方式傳遞進來的
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
//確實是fuck出來的吧,我沒騙你吧~不對,是fork出來的 -_-|||
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
handleSystemServerProcess(parsedArgs);
}
return true;
}
ActivityManagerService是什麼?什麼時候初始化的?有什麼作用?
ActivityManagerService,簡稱AMS,服務端物件,負責系統中所有Activity的生命週期。
ActivityManagerService進行初始化的時機很明確,就是在SystemServer程序開啟的時候,就會初始化ActivityManagerService。從下面的程式碼中可以看到
public final class SystemServer {
//zygote的主入口
public static void main(String[] args) {
new SystemServer().run();
}
public SystemServer() {
// Check for factory test mode.
mFactoryTestMode = FactoryTest.getMode();
}
private void run() {
...ignore some code...
//載入本地系統服務庫,並進行初始化
System.loadLibrary("android_servers");
nativeInit();
// 建立系統上下文
createSystemContext();
//初始化SystemServiceManager物件,下面的系統服務開啟都需要呼叫SystemServiceManager.startService(Class<T>),這個方法通過反射來啟動對應的服務
mSystemServiceManager = new SystemServiceManager(mSystemContext);
//開啟服務
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
...ignore some code...
}
//初始化系統上下文物件mSystemContext,並設定預設的主題,mSystemContext實際上是一個ContextImpl物件。呼叫ActivityThread.systemMain()的時候,會呼叫ActivityThread.attach(true),而在attach()裡面,則建立了Application物件,並呼叫了Application.onCreate()。
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
//在這裡開啟了幾個核心的服務,因為這些服務之間相互依賴,所以都放在了這個方法裡面。
private void startBootstrapServices() {
...ignore some code...
//初始化ActivityManagerService
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
//初始化PowerManagerService,因為其他服務需要依賴這個Service,因此需要儘快的初始化
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
// 現在電源管理已經開啟,ActivityManagerService負責電源管理功能
mActivityManagerService.initPowerManagement();
// 初始化DisplayManagerService
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
//初始化PackageManagerService
mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
...ignore some code...
}
}
經過上面這些步驟,我們的ActivityManagerService物件已經建立好了,並且完成了成員變數初始化。而且在這之前,呼叫createSystemContext()建立系統上下文的時候,也已經完成了mSystemContext和ActivityThread的建立。注意,這是系統程序開啟時的流程,在這之後,會開啟系統的Launcher程式,完成系統介面的載入與顯示。
你是否會好奇,我為什麼說AMS是服務端物件?下面我給你介紹下Android系統裡面的伺服器和客戶端的概念。
其實伺服器客戶端的概念不僅僅存在於Web開發中,在Android的框架設計中,使用的也是這一種模式。伺服器端指的就是所有App共用的系統服務,比如我們這裡提到的ActivityManagerService,和前面提到的PackageManagerService、WindowManagerService等等,這些基礎的系統服務是被所有的App公用的,當某個App想實現某個操作的時候,要告訴這些系統服務,比如你想開啟一個App,那麼我們知道了包名和MainActivity類名之後就可以開啟
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
startActivity(intent);
但是,我們的App通過呼叫startActivity()並不能直接開啟另外一個App,這個方法會通過一系列的呼叫,最後還是告訴AMS說:“我要開啟這個App,我知道他的住址和名字,你幫我開啟吧!”所以是AMS來通知zygote程序來fork一個新程序,來開啟我們的目標App的。這就像是瀏覽器想要開啟一個超連結一樣,瀏覽器把網頁地址傳送給伺服器,然後還是伺服器把需要的資原始檔傳送給客戶端的。
知道了Android Framework的客戶端伺服器架構之後,我們還需要了解一件事情,那就是我們的App和AMS(SystemServer程序)還有zygote程序分屬於三個獨立的程序,他們之間如何通訊呢?
App與AMS通過Binder進行IPC通訊,AMS(SystemServer程序)與zygote通過Socket進行IPC通訊。
那麼AMS有什麼用呢?在前面我們知道了,如果想開啟一個App的話,需要AMS去通知zygote程序,除此之外,其實所有的Activity的開啟、暫停、關閉都需要AMS來控制,所以我們說,AMS負責系統中所有Activity的生命週期。
在Android系統中,任何一個Activity的啟動都是由AMS和應用程式程序(主要是ActivityThread)相互配合來完成的。AMS服務統一排程系統中所有程序的Activity啟動,而每個Activity的啟動過程則由其所屬的程序具體來完成。
這樣說你可能還是覺得比較抽象,沒關係,下面有一部分是專門來介紹AMS與ActivityThread如何一起合作控制Activity的生命週期的。
Launcher是什麼?什麼時候啟動的?
當我們點選手機桌面上的圖示的時候,App就由Launcher開始啟動了。但是,你有沒有思考過Launcher到底是一個什麼東西?
Launcher本質上也是一個應用程式,和我們的App一樣,也是繼承自Activity
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
public final class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener {
}
Launcher實現了點選、長按等回撥介面,來接收使用者的輸入。既然是普通的App,那麼我們的開發經驗在這裡就仍然適用,比如,我們點選圖示的時候,是怎麼開啟的應用呢?如果讓你,你怎麼做這個功能呢?捕捉圖示點選事件,然後startActivity()傳送對應的Intent請求唄!是的,Launcher也是這麼做的,就是這麼easy!
那麼到底是處理的哪個物件的點選事件呢?既然Launcher是App,並且有介面,那麼肯定有佈局檔案呀,是的,我找到了佈局檔案launcher.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:id="@+id/launcher">
<com.android.launcher2.DragLayer
android:id="@+id/drag_layer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
<include
android:id="@+id/dock_divider"
layout="@layout/workspace_divider"
android:layout_marginBottom="@dimen/button_bar_height"
android:layout_gravity="bottom" />
<include
android:id="@+id/paged_view_indicator"
layout="@layout/scroll_indicator"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/button_bar_height" />
<!-- The workspace contains 5 screens of cells -->
<com.android.launcher2.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/workspace_left_padding"
android:paddingEnd="@dimen/workspace_right_padding"
android:paddingTop="@dimen/workspace_top_padding"
android:paddingBottom="@dimen/workspace_bottom_padding"
launcher:defaultScreen="2"
launcher:cellCountX="@integer/cell_count_x"
launcher:cellCountY="@integer/cell_count_y"
launcher:pageSpacing="@dimen/workspace_page_spacing"
launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
<include android:id="@+id/cell3" layout="@layout/workspace_screen" />
<include android:id="@+id/cell4" layout="@layout/workspace_screen" />
<include android:id="@+id/cell5" layout="@layout/workspace_screen" />
</com.android.launcher2.Workspace>
...ignore some code...
</com.android.launcher2.DragLayer>
</FrameLayout>
為了方便檢視,我刪除了很多程式碼,從上面這些我們應該可以看出一些東西來:Launcher大量使用標籤來實現介面的複用,而且定義了很多的自定義控制元件實現介面效果,dock_divider從佈局的引數宣告上可以猜出,是底部操作欄和上面圖示佈局的分割線,而paged_view_indicator則是頁面指示器,和App首次進入的引導頁下面的介面引導是一樣的道理。當然,我們最關心的是Workspace這個佈局,因為註釋裡面說在這裡麵包含了5個螢幕的單元格,想必你也猜到了,這個就是在首頁存放我們圖示的那五個介面(不同的ROM會做不同的DIY,數量不固定)。
接下來,我們應該開啟workspace_screen佈局,看看裡面有什麼東東。
workspace_screen.xml
<com.android.launcher2.CellLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/cell_layout_left_padding"
android:paddingEnd="@dimen/cell_layout_right_padding"
android:paddingTop="@dimen/cell_layout_top_padding"
android:paddingBottom="@dimen/cell_layout_bottom_padding"
android:hapticFeedbackEnabled="false"
launcher:cellWidth="@dimen/workspace_cell_width"
launcher:cellHeight="@dimen/workspace_cell_height"
launcher:widthGap="@dimen/workspace_width_gap"
launcher:heightGap="@dimen/workspace_height_gap"
launcher:maxGap="@dimen/workspace_max_gap" />
裡面就一個CellLayout,也是一個自定義佈局,那麼我們就可以猜到了,既然可以存放圖示,那麼這個自定義的佈局很有可能是繼承自ViewGroup或者是其子類,實際上,CellLayout確實是繼承自ViewGroup。在CellLayout裡面,只放了一個子View,那就是ShortcutAndWidgetContainer。從名字也可以看出來,ShortcutAndWidgetContainer這個類就是用來存放快捷圖示和Widget小部件的,那麼裡面放的是什麼物件呢?
在桌面上的圖示,使用的是BubbleTextView物件,這個物件在TextView的基礎之上,添加了一些特效,比如你長按移動圖示的時候,圖示位置會出現一個背景(不同版本的效果不同),所以我們找到BubbleTextView物件的點選事件,就可以找到Launcher如何開啟一個App了。
除了在桌面上有圖示之外,在程式列表中點選圖示,也可以開啟對應的程式。這裡的圖示使用的不是BubbleTextView物件,而是PagedViewIcon物件,我們如果找到它的點選事件,就也可以找到Launcher如何開啟一個App。
其實說這麼多,和今天的主題隔著十萬八千里,上面這些東西,你有興趣就看,沒興趣就直接跳過,不知道不影響這篇文章閱讀。
BubbleTextView的點選事件在哪裡呢?我來告訴你:在Launcher.onClick(View v)裡面。
/**
* Launches the intent referred by the clicked shortcut
*/
public void onClick(View v) {
...ignore some code...
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
//開始開啟Activity咯~
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
//如果點選的是圖示資料夾,就開啟資料夾
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
...ignore some code...
}
}
從上面的程式碼我們可以看到,在桌面上點選快捷圖示的時候,會呼叫
startActivitySafely(v, intent, tag);
那麼從程式列表介面,點選圖示的時候會發生什麼呢?實際上,程式列表介面使用的是AppsCustomizePagedView物件,所以我在這個類裡面找到了onClick(View v)。
com.android.launcher2.AppsCustomizePagedView.java
/**
* The Apps/Customize page that displays all the applications, widgets, and shortcuts.
*/
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
View.OnClickListener, View.OnKeyListener, DragSource,
PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
LauncherTransitionable {
@Override
public void onClick(View v) {
...ignore some code...
if (v instanceof PagedViewIcon) {
mLauncher.updateWallpaperVisibility(true);
mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
} else if (v instanceof PagedViewWidget) {
...ignore some code..
}
}
}
可以看到,呼叫的是
mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
和上面一樣!這叫什麼?這叫殊途同歸!
所以咱們現在又明白了一件事情:不管從哪裡點選圖示,呼叫的都是Launcher.startActivitySafely()。
- 下面我們就可以一步步的來看一下Launcher.startActivitySafely()到底做了什麼事情。
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
呼叫了startActivity(v, intent, tag)
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
if (useLaunchAnimation) {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent, opts.toBundle());
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(),
opts.toBundle());
}
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
...
}
return false;
}
這裡會呼叫Activity.startActivity(intent, opts.toBundle()),這個方法熟悉嗎?這就是我們經常用到的Activity.startActivity(Intent)的過載函式。而且由於設定了
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
所以這個Activity會新增到一個新的Task棧中,而且,startActivity()呼叫的其實是startActivityForResult()這個方法。
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
所以我們現在明確了,Launcher中開啟一個App,其實和我們在Activity中直接startActivity()基本一樣,都是呼叫了Activity.startActivityForResult()。
Instrumentation是什麼?和ActivityThread是什麼關係?
還記得前面說過的Instrumentation物件嗎?每個Activity都持有Instrumentation物件的一個引用,但是整個程序只會存在一個Instrumentation物件。當startActivityForResult()呼叫之後,實際上還是呼叫了mInstrumentation.execStartActivity()
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
...ignore some code...
} else {
if (options != null) {
//當現在的Activity有父Activity的時候會呼叫,但是在startActivityFromChild()內部實際還是呼叫的mInstrumentation.execStartActivity()
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
...ignore some code...
}
下面是mInstrumentation.execStartActivity()的實現
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
...ignore some code...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}
所以當我們在程式中呼叫startActivity()的 時候,實際上呼叫的是Instrumentation的相關的方法。
Instrumentation意為“儀器”,我們先看一下這個類裡面包含哪些方法吧
我們可以看到,這個類裡面的方法大多數和Application和Activity有關,是的,這個類就是完成對Application和Activity初始化和生命週期的工具類。比如說,我單獨挑一個callActivityOnCreate()讓你看看
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
對activity.performCreate(icicle);這一行程式碼熟悉嗎?這一行裡面就呼叫了傳說中的Activity的入口函式onCreate(),不信?接著往下看
Activity.performCreate()
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
沒騙你吧,onCreate在這裡呼叫了吧。但是有一件事情必須說清楚,那就是這個Instrumentation類這麼重要,為啥我在開發的過程中,沒有發現他的蹤跡呢?
是的,Instrumentation這個類很重要,對Activity生命週期方法的呼叫根本就離不開他,他可以說是一個大管家,但是,這個大管家比較害羞,是一個女的,管內不管外,是老闆娘~
那麼你可能要問了,老闆是誰呀?
老闆當然是大名鼎鼎的ActivityThread了!
ActivityThread你都沒聽說過?那你肯定聽說過傳說中的UI執行緒吧?是的,這就是UI執行緒。我們前面說過,App和AMS是通過Binder傳遞資訊的,那麼ActivityThread就是專門與AMS的外交工作的。
AMS說:“ActivityThread,你給我暫停一個Activity!”
ActivityThread就說:“沒問題!”然後轉身和Instrumentation說:“老婆,AMS讓暫停一個Activity,我這裡忙著呢,你快去幫我把這事辦了把~”
於是,Instrumentation就去把事兒搞定了。
所以說,AMS是董事會,負責指揮和排程的,ActivityThread是老闆,雖然說家裡的事自己說了算,但是需要聽從AMS的指揮,而Instrumentation則是老闆娘,負責家裡的大事小事,但是一般不拋頭露面,聽一家之主ActivityThread的安排。
如何理解AMS和ActivityThread之間的Binder通訊?
前面我們說到,在呼叫startActivity()的時候,實際上呼叫的是
mInstrumentation.execStartActivity()
但是到這裡還沒完呢!裡面又呼叫了下面的方法
ActivityManagerNative.getDefault()
.startActivity
這裡的ActivityManagerNative.getDefault返回的就是ActivityManagerService的遠端介面,即ActivityManagerProxy。
怎麼知道的呢?往下看
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
//從類宣告上,我們可以看到ActivityManagerNative是Binder的一個子類,而且實現了IActivityManager介面
static public IActivityManager getDefault() {
return gDefault.get();
}
//通過單例模式獲取一個IActivityManager物件,這個物件通過asInterface(b)獲得
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
}
//最終返回的還是一個ActivityManagerProxy物件
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
//這裡面的Binder型別的obj引數會作為ActivityManagerProxy的成員變數儲存為mRemote成員變數,負責進行IPC通訊
return new ActivityManagerProxy(obj);
}
}
再看ActivityManagerProxy.startActivity(),在這裡面做的事情就是IPC通訊,利用Binder物件,呼叫transact(),把所有需要的引數封裝成Parcel物件,向AMS傳送資料進行通訊。
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
Binder本質上只是一種底層通訊方式,和具體服務沒有關係。為了提供具體服務,Server必須提供一套介面函式以便Client通過遠端訪問使用各種服務。這時通常採用Proxy設計模式:將介面函式定義在一個抽象類中,Server和Client都會以該抽象類為基類實現所有介面函式,所不同的是Server端是真正的功能實現,而Client端是對這些函式遠端呼叫請求的包裝。
為了更方便的說明客戶端和伺服器之間的Binder通訊,下面以ActivityManagerServices和他在客戶端的代理類ActivityManagerProxy為例。
ActivityManagerServices和ActivityManagerProxy都實現了同一個介面——IActivityManager。
class ActivityManagerProxy implements IActivityManager{}
public final class ActivityManagerService extends ActivityManagerNative{}
public abstract class ActivityManagerNative extends Binder implements IActivityManager{}
雖然都實現了同一個介面,但是代理物件ActivityManagerProxy並不會對這些方法進行真正地實現,ActivityManagerProxy只是通過這種方式對方法的引數進行打包(因為都實現了相同介面,所以可以保證同一個方法有相同的引數,即對要傳輸給伺服器的資料進行打包),真正實現的是ActivityManagerService。
但是這個地方並不是直接由客戶端傳遞給伺服器,而是通過Binder驅動進行中轉。其實我對Binder驅動並不熟悉,我們就把他當做一箇中轉站就OK,客戶端呼叫ActivityManagerProxy接口裡面的方法,把資料傳送給Binder驅動,然後Binder驅動就會把這些東西轉發給伺服器的ActivityManagerServices,由ActivityManagerServices去真正的實施具體的操作。
但是Binder只能傳遞資料,並不知道是要呼叫ActivityManagerServices的哪個方法,所以在資料中會新增方法的唯一標識碼,比如前面的startActivity()方法:
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
...ignore some code...
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
上面的START_ACTIVITY_TRANSACTION就是方法標示,data是要傳輸給Binder驅動的資料,reply則接受操作的返回值。
即
客戶端:ActivityManagerProxy =====>Binder驅動=====> ActivityManagerService:伺服器
而且由於繼承了同樣的公共介面類,ActivityManagerProxy提供了與ActivityManagerService一樣的函式原型,使使用者感覺不出Server是執行在本地還是遠端,從而可以更加方便的呼叫這些重要的系統服務。
但是!這裡Binder通訊是單方向的,即從ActivityManagerProxy指向ActivityManagerService的,如果AMS想要通知ActivityThread做一些事情,應該咋辦呢?
還是通過Binder通訊,不過是換了另外一對,換成了ApplicationThread和ApplicationThreadProxy。
客戶端:ApplicationThread <=====Binder驅動<===== ApplicationThreadProxy:伺服器
他們也都實現了相同的介面IApplicationThread
private class ApplicationThread extends ApplicationThreadNative {}
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread{}
class ApplicationThreadProxy implements IApplicationThread {}
剩下的就不必多說了吧,和前面一樣。
AMS接收到客戶端的請求之後,會如何開啟一個Activity?
OK,至此,點選桌面圖示呼叫startActivity(),終於把資料和要開啟Activity的請求傳送到了AMS了。說了這麼多,其實這些都在一瞬間完成了,下面咱們研究下AMS到底做了什麼。
注:前方有高能的方法呼叫鏈,如果你現在累了,請先喝杯咖啡或者是上趟廁所休息下
AMS收到startActivity的請求之後,會按照如下的方法鏈進行呼叫
呼叫startActivity()
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, options,
UserHandle.getCallingUserId());
}
呼叫startActivityAsUser()
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
...ignore some code...
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, userId,
相關推薦
【凱子哥帶你學Framework】Activity啟動過程全解析
It’s right time to learn Android’s Framework !
前言
一個App是怎麼啟動起來的?
App的程式入口到底是哪裡?
Launcher到底是什麼神奇的東西?
聽說還有個AMS的東西,它是做什麼的?
【凱子哥帶你學Framework】Activity介面顯示全解析
前幾天凱子哥寫的Framework層的解析文章《Activity啟動過程全解析》,反響還不錯,這說明“寫讓大家都能看懂的Framework解析文章”的思想是基本正確的。
我個人覺得,深入分析的文章必不可少,但是對於更多的Android開發者——即
棟哥帶你學Java執行緒和鎖
執行緒
在說執行緒之前我們瞭解下什麼是程序.
程序:一個正在執行的程式(獨立執行).
程序和執行緒之間的關係:
一個程序可以只有一個執行緒(單執行緒程式),一個執行緒中也可以有多個執行緒(多執行緒程式).
執行緒:一個執行緒,相當於cpu的執行路徑,一個獨
棟哥帶你學Java泛型
在說泛型之前先建三個類.
第一個、Person類
/*
* 姓名 和 年齡
* 構造 set/get toString
* 建立一個學生類(構造方法) 繼承人類
*/
public class Person {
private Stri
棟哥帶你學Java跟蹤行號流、裝飾者模式以及列印流
跟蹤行號流
跟蹤行號字元輸入流:LineNumberReader
這個流有什麼特點呢?
沒什麼特別的特點,就是可以獲取行號.
public void fun() throws FileNotFound
棟哥帶你學Java抽獎小遊戲
使用者資訊類:
public class User {
static String username; // 註冊時存入的使用者名稱
static String password; //
棟哥帶你學Java執行緒中斷
執行緒中斷
(一)stop方法
在早期,執行緒中又一個stop方法,可以直接終止執行緒.但是現在已經不用了,而且api上也已經在方法下標明已過時.這時因為stop方法一旦呼叫,不管執行緒是什麼狀態的都直接會被終
止,假如正在執行執行緒,run方法中的程式
棟哥帶你學Java static的用法
一、封裝
在說static關鍵詞的用法之前我們先說說Java面向物件的三大特徵之一的封裝.
什麼是封裝?
講類中的屬性或方法對外界隱藏,然後開放公共的訪問方式.
那麼怎麼隱藏呢?
之前我們寫mai
棟哥帶你學Java集合與迭代器
Java中的集合
為什麼會有集合?
因為陣列有弊端,所有才有了集合.
陣列的特點是什麼?
首先,陣列中只能新增相同型別的元素(基本資料型別和引用資料型別都能儲存),而且陣列的長度一旦確定就不能該改變,
如果要新增超出陣列長度個數的元素,就只能再宣告一個新的
棟哥帶你學Java之JDBC
JDBC
什麼是JDBC?
JDBC(Java Data Base Connectivity,java資料庫連線)是一種用於執行SQL語句
的JavaAPI,可以為多種關係資料庫提供統一訪問,它由一組用Java語言編寫的類和介面組成.JDBC提供了一種基準
程哥帶你學python-[第一章-初識Python]
license div 高級 今天 做什麽 挖掘 運維 自然語言 什麽 Python是一種解釋型、面向對象、動態數據類型的高級程序設計語言。
Python由Guido van Rossum於1989年底發明,第一個公開發行版發行於1991年。
像Perl語言一樣, Py
【昊昊帶你學】android-解決/data/dalvik-cache佔用記憶體的問題
較新款android手機應該不存在此類問題。一般都是記憶體容量較小的老android手機會有這個問題。
安裝軟體的空間非常小,可以通過app2sd把軟體移到sd卡上(sd卡上分一個ext2分割槽)。有時候做完app2sd後可能依舊發現空間沒有變大多少。
觀察/da
【昊昊帶你學】VMWare+Mountain Lion
直接黑蘋果看起來挺美好,吃起來挺困難,所以,如果你只是準備用Mac來做一些碼農的活,而不是很追求效能的話,或許可以考慮一下虛擬機器裝Mac。
言歸正傳,折騰了許久,終於在虛擬機器下裝好了山獅。
第一步有傳言某些CPU不支援虛擬化,不過我是沒有遇到什麼問題,所以沒有
【牛客帶你學程式設計C++方向】專案練習第1期
//普通構造
MyString::MyString(const char *str){
if(str == NULL){
m_data = new char[1]; /
【昊昊帶你學】基本資料結構(下)
連結串列
前面大家用隊、棧的時候用的都是陣列。陣列挺好用的,不過蛋疼的是,數組裡面要增、刪資料就蛋疼了。不過咱們有連結串列,下面我來醜陋地給大家畫個連結串列~
哈哈,說了畫給大家,就果斷要親手畫,果然很醜陋的說 O_o
我來簡單給大家先說一下連結串列的每個節點都
【昊昊帶你學】寬搜(BFS)
**************************轉載請註明出處!******************************
很久很久以前,我講了深搜(這到底是有多久 - -|||)。對應的當然就得有寬搜。搜尋的概念上次深搜已經講過了,可以去“複習
【昊昊帶你學】演算法概述
什麼是演算法
雖然這篇題目叫概述,不過本菜只是不知道該叫什麼好,我這種小白腫麼敢概述演算法是不。就是總體上說說我對演算法的理解吧 O_o
演算法可能有些初學童鞋接觸過。有些地方高中教材裡會涉及到(江蘇是有的)。《演算法導論》給出了一個定
【江哥帶你從“零”玩轉Html5 + 跨平臺開發】學習筆記
最近一段時間都沒有寫部落格,一是因為有新的專案要做,而是最近在學習有關H5移動開發相關方面的內容,以下是看了“【江哥帶你從“零”玩轉Html5 + 跨平臺開發】”總結的學習筆記,目前只是更新到了H5+CSS3,相關視訊網站地址:http://bbs.520it.com/fo
【宇哥帶你玩轉MySQL】索引篇(一)索引揭祕,看他是如何讓你的查詢效能指數提升的
場景復現,一個索引提高600倍查詢速度?
首先準備一張books表
create table books(
id int not null primary key auto_increment,
name varchar(255) not null,
author va
為什麼MySQL要用B+樹?聊聊B+樹與硬碟的前世今生【宇哥帶你玩轉MySQL 索引篇(二)】
為什麼MySQL要用B+樹?聊聊B+樹與硬碟的前世今生
在上一節,我們聊到資料庫為了讓我們的查詢加速,通過索引方式對資料進行冗餘並排序,這樣我們在使用時就可以在排好序的資料裡進行快速的二分查詢,使得查詢效率指數提升。但是我在結尾同樣提到一個問題,就是記憶體大小一般是很有限的,不可能把一個表所有的