前面我們分析了init程序,zygote程序,SystemServer程序,本篇的Launcher是系統啟動流程的最後一個程序。
Launcher程序是一個系統的應用程式,位於packages/apps/Launcher3中,它用於顯示已經安裝的應用程式,它通過存取PackageManagerService獲取安裝的應用程式,然後將他們封裝成一個個的快捷圖示顯示到螢幕上,每一個圖示包含了被啟動應用程式的Intent資訊,點選之後就可以啟動對應應用程式。
private void startOtherServices() {
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
...
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
}
}
}
Launcher的入口函數在System.startOtherServices方法中,我們進入systemReady方法中:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback) {
...
mStackSupervisor.resumeFocusedStackTopActivityLocked();
...
}
進入到了ActivityStackSupervisor.resumeFocusedStackTopActivityLocked中
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean resumeFocusedStackTopActivityLocked() {
return resumeFocusedStackTopActivityLocked(null, null, null);
}
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (targetStack != null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || r.state != RESUMED) {
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
}
return false;
}
在上面最後呼叫到了mFocusedStack.resumeTopActivityUncheckedLocked
,mFocusedStack是ActivityStack型別的,我們進入檢視原始碼:
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
}
boolean result = false;
try {
// Protect against recursion.
mStackSupervisor.inResumeTopActivity = true;
if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
mService.updateSleepIfNeededLocked();
}
result = resumeTopActivityInnerLocked(prev, options);
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
return result;
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...
final TaskRecord prevTask = prev != null ? prev.task : null;
if (next == null) {
/***
* 這裡啟動了Launcher
*/
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
}
}
上面從resumeTopActivityUncheckedLocked呼叫到了resumeTopActivityInnerLocked中,最後呼叫到了ActivityStackSupervisor.resumeHomeStackTask函數。
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) {
...
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
mService.setFocusedActivityLocked(r, myReason);
return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
return mService.startHomeActivityLocked(mCurrentUser, myReason);
}
接著進入到了ActivityManagerService.startHomeActivityLocked
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
// 1
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
在註釋1處的getHomeIntent方法程式碼如下:
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);//1
}
return intent;
}
在註釋1處新增了一個CATEGORY_HOME
,CATEGORY_HOME = "android.intent.category.HOME"
,這裡我們鎖定就是啟動了Launcher頁面,因為我們檢視系統應用Launcher的清單檔案中匹配了它,Launcher的清單檔案如下:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="16"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
...
<application
android:allowBackup="@bool/enable_backup"
android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher_home"
android:label="@string/app_name"
android:largeHeap="@bool/config_largeHeap"
android:restoreAnyVersion="true"
android:supportsRtl="true" >
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/Theme"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="nosensor"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>
</activity>
</application>
</manifest>
從上面可以看出Launcher的intent-filter匹配了上面的Intent,所以Launcher就被啟動起來了。就會執行onCreate方法。
Launcher.onCreate函數如下:
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1
LauncherAppState app = LauncherAppState.getInstance();
//2
mModel = app.setLauncher(this);
setContentView(R.layout.launcher);
if (!mRestoring) {
//3
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
}
註釋1處單例獲取LauncherAppState的範例,在註釋2處呼叫它的setLauncher函數並將Launcher物件傳入,setLauncher函數如下所示:
packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
new LauncherAccessibilityDelegate(launcher) : null;
return mModel;
}
接著呼叫到了LauncherModel的initialize函數:
packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java
/**
* Set this as the current Launcher activity object for the loader.
*/
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
// Disconnect any of the callbacks and drawables associated with ItemInfos on the
// workspace to prevent leaking Launcher activities on orientation change.
unbindItemInfosAndClearQueuedBindRunnables();
mCallbacks = new WeakReference<Callbacks>(callbacks);
}
}
我們看到上面其實就是設定了一個回撥函數,回撥函數就是Launcher自身,不過是以一個弱參照的方式,這個回撥後面會用到。
接著分析onCreate中的註釋3的部分,launcherModel.startLoader方法:
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
public void startLoader(int synchronousBindPage, int loadFlags) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
synchronized (mDeferredBindRunnables) {
mDeferredBindRunnables.clear();
}
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
// If there is already one running, tell it to stop.
stopLoaderLocked();
mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}
}
上面建立了一個HandlerThread的子執行緒的Handler,用來處理耗時任務,在上面的最後post了一個任務,這個任務的類是LoaderTask.java,實現了Runnable介面,run方法如下:
public void run() {
synchronized (mLock) {
if (mStopped) {
return;
}
mIsLoaderTaskRunning = true;
}
// Optimize for end-user experience: if the Launcher is up and // running with the
// All Apps interface in the foreground, load All Apps first. Otherwise, load the
// workspace first (default).
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
//1
loadAndBindWorkspace();
if (mStopped) {
break keep_running;
}
waitForIdle();
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
// 2
loadAndBindAllApps();
}
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
mHasLoaderCompletedOnce = true;
}
}
上面的兩個註釋寫的很清楚,在註釋1處loadAndBindWorkspace是載入工作空間,在註釋2處loadAndBindAllApps用來載入所有的app。
loadAndBindAllApps原始碼如下:
private void loadAndBindAllApps() {
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
}
if (!mAllAppsLoaded) {
// 1
loadAllApps();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
}
updateIconCache();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
mAllAppsLoaded = true;
}
} else {
onlyBindAllApps();
}
}
註釋1處呼叫了loadAllApps(),原始碼如下:
private void loadAllApps() {
...
mHandler.post(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(added);//1
...
}
}
});
...
}
這裡的callbacks就是上面弱參照儲存的Launcher,所以我們直接檢視Launcher中的bindAllApplications,程式碼如下:
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
public void bindAllApplications(final ArrayList<AppInfo> apps) {
if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
mTmpAppsList = apps;
return;
}
if (mAppsView != null) {
//1
mAppsView.setApps(apps);
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
在註釋1處呼叫了 mAppsView的setApps,mAppsView是一個AllAppsContainerView型別的,我們檢視它的setApps方法:
packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java
private final AlphabeticalAppsList mApps;
public void setApps(List<AppInfo> apps) {
mApps.setApps(apps);
}
這裡我們檢視onFinishInflate函數
private AllAppsRecyclerView mAppsRecyclerView;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//1 初始化控制元件
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
//2 傳遞應用
mAppsRecyclerView.setApps(mApps);
mAppsRecyclerView.setLayoutManager(mLayoutManager);
//3 設定adapter
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
mAppsRecyclerView.addOnScrollListener(mElevationController);
mAppsRecyclerView.setElevationController(mElevationController);
}
onFinishInflate在xml載入完成之後就會呼叫,這裡launcher中app的顯示邏輯和我們平時將資料顯示到一個RecyclerView上基本一致,所以這裡也比較好理解。
在此對整個Android系統啟動流程做一個全面的總結:
1 按Power鍵啟動電源及系統啟動
當按下電源鍵,引導晶片程式碼開始從固化在ROM中預定義的地方開始執行,載入載入程式Bootloader到RAM,然後執行載入程式。
2 載入程式Bootloader
載入程式是Android作業系統被拉起來之前的一個程式,類似於window一樣,它的作用就是把系統拉起執行起來。它是針對特定的主機板與晶片的。裝置製造商要麼使用很受歡迎的載入程式比如redboot、uboot、qibootloader或者開發自己的載入程式,它不是Android作業系統的一部分。載入程式是OEM廠商或者運營商加鎖和限制的地方。
3 linux核心啟動
Android核心與桌面linux核心啟動的方式差不多。核心啟動時,設定快取、被保護記憶體、計劃列表,載入驅動。當核心完成系統設定,它首先在系統檔案中尋找」init」檔案,然後啟動root程序或者系統的第一個程序。
4 init程序啟動
建立和掛載啟動所需要的檔案目錄,初始化和啟動屬性服務,解析init.rc設定並且啟動Zygote程序。
5 Zygote程序啟動
建立JavaVM併為JavaVM註冊JNI,建立伺服器端Socket,啟動SystemServer程序。
6 SystemServer程序啟動
啟動Binder執行緒池和SystemServiceManager,並且啟動各種系統服務。
7 Launcher啟動
被SystemServer程序啟動的ActivityManagerService會啟動Launcher,Launcher啟動後會將已安裝應用的快捷圖示顯示到介面上。
1 Android系統啟動流程? 答案如上所述
2 什麼是寫時拷貝(copy- on-write)?
寫時拷貝copy_on_write技術: fork的實現是使用copy_on_write技術實現的,它是一種可以推遲甚至避免拷貝的技術,核心此時並不複製整個程序的地址空間,而是讓父子程序共用同一個地址空間,只有在子程序真正需要寫入資料的時候才會賦值地址空間,從而使得每個程序擁有自己的程序空間,也就是說資源的複製是在需要寫入資料的時候才會進行,在此之前只會以唯讀的形式共用。
3 孤兒程序和殭屍程序是什麼?
fork系統呼叫之後,父子程序將交替執行,執行順序不定。如果父程序先退出,子程序還沒退出那麼子程序的父程序將變為init程序(託孤給了init程序)。(注:任何一個程序都必須有父程序)如果子程序先退出,父程序還沒退出,那麼子程序必須等到父程序捕獲到了子程序的退出狀態才真正結束,否則這個時候子程序就成為僵程序(殭屍程序:只保留一些退出資訊供父程序查詢).
4 system_server 為什麼要在 Zygote 中啟動,而不是由 init 直接啟動呢?
Zygote 作為一個孵化器,可以提前載入一些資源,這樣 fork() 時基於 Copy-On-Write 機制建立的其他程序就能直接使用這些資源,而不用重新載入。比如 system_server 就可以直接使用 Zygote 中的 JNI函數、共用庫、常用的類、以及主題資源。
5 為什麼要專門使用 Zygote 程序去孵化應用程序,而不是讓 system_server 去孵化呢?
system_server 相比 Zygote 多執行了 AMS、WMS 等服務,這些對一個應用程式來說是不需要的。另外程序的 fork() 對多執行緒不友好,僅會將發起呼叫的執行緒拷貝到子程序,這可能會導致死鎖,而system_server 中肯定是有很多執行緒的。
6 多執行緒的程序fork呼叫為什麼會導致死鎖?
多執行緒的程序的fork呼叫:複製整個使用者空間的資料(通常使用 copy-on-write 的策略,
所以可以實現的速度很快)以及所有系統物件,然後僅複製當前執行緒到子程序。這裡:所有父程序中別的執行緒,到了子程序中都是突然蒸發掉的假設這麼一個環境,在 fork 之前,有一個子執行緒 lock 了某個鎖,獲得了對鎖的所有權。fork 以後,在子程序中,所有的額外執行緒都人間蒸發了。而鎖卻被正常複製了,在子程序看來,這個鎖沒有主人,所以沒有任何人可以對它解鎖。當子程序想 lock 這個鎖時,不再有任何手段可以解開了。程式發生死鎖
7 Zygote 為什麼不採用 Binder 機制進行 IPC 通訊?
Binder 機制中存在 Binder 執行緒池,是多執行緒的,如果 Zygote 採用 Binder 的話就存在上面說的fork() 與 多執行緒的問題了。其實嚴格來說,Binder 機制不一定要多執行緒,所謂的 Binder 執行緒只不過是在迴圈讀取 Binder 驅動的訊息而已,只註冊一個 Binder 執行緒也是可以工作的,比如 service manager就是這樣的。實際上 Zygote 儘管沒有采取 Binder 機制,它也不是單執行緒的,但它在 fork() 前主動停止了其他執行緒,fork() 後重新啟動了。