通過進階解密學習 記錄 Launcher 啟動過程,記錄加深了解後續其他Android版本的啟動流程
系統啟動的最後一步時啟動一個應用程式用來顯示系統中已經安裝的應用程式,這個應用程式就叫做Launcher。Launcher在啟動過程中會請求PackageManagerService傳回系統中已經安裝的應用程式的資訊,并将這些資訊封裝成一個快捷圖示清單顯示在系統螢幕上,這樣使用者可以通過點選這些快捷圖示來啟動相應的應用程式。
時序圖如下
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yN2UTZ4QTOzQzMllDZlNTNkRTZzQ2M1gTM4UTYyY2Y28CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Launcher的啟動過程
Launcher 的入口為 AMS 的 systemRead 函數,它在 SystemServer 的 startOtherServices 函數中被調用
//com.android.server SystemServer.java
public void startOtherServices(){
...
mActivityManagerService.systemReady(() -> {
...
}
}
在AMS 的 systemRead 函數實作
//com.android.server.am ActivityManagerService.java
public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
...
synchronized(this) {
...
1.
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}
//package 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)) {
2
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
...
return false;
}
//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;
3
result = resumeTopActivityInnerLocked(prev, options);
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
mStackSupervisor.checkReadyForSleepLocked();
return result;
}
//com.android.server.am; ActivityStack.java
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...
return isOnHomeDisplay() &&
4
mStackSupervisor.resumeHomeStackTask(prev, "prevFinished");
}
...
}
//com.android.server.am; ActivityStackSupervisor.java
boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
...
if (prev != null) {
prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
}
mHomeStack.moveHomeStackTaskToTop();
擷取 HomeActivity
ActivityRecord r = getHomeActivity();
final String myReason = reason + " resumeHomeStackTask";
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
moveFocusableActivityStackToFrontLocked(r, myReason);
return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
//調用 AMS startHomeActivityLocked 函數
5
return mService.startHomeActivityLocked(mCurrentUser, myReason);
}
從上述的代碼中主要 通過中AMS 的 systemRead .調resumeFocusedStackTopActivityLocked(); 1至5 處 層層調用,最後通過ActivityManagerService 建立Laucncher啟動的所需的Intent
//com.android.server.am; ActivityManagerService.java
boolean startHomeActivityLocked(int userId, String reason) {
1 判斷mFactoryTest的工廠模式 和mTopAction 的值
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
2. 建立 Launcher 所需要的 Intent
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) { 3
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
4 啟動的應用程式就是 Launcher,
mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
上面代碼主要做了一下幾件事:
1 FactoryTest: 代表啟動的運作模式:非工廠模式、低級工廠模式、進階工廠模式 , mTopAction: 則描述第一個被啟動 Activity 元件的 Action,預設是 Intent.ACTION_MAIN.
判斷目前FactoryTest 為低級工廠模式 并且 mTopAction 為空的時候 傳回 false
2 建立 Launcher 所需要的 Intent
3 判斷符合 action 為 Intent.ACTION_MAIN、Category 為 Intent.CATEGORY_HOME 的應用程式是否已經啟動
4 如果沒有啟動,則啟動應用
回頭在看 getHomeIntent() 怎麼設定 Launcher 的 Intent
//com.android.server.am; ActivityManagerService.java
Intent getHomeIntent() {
//傳入第一個啟動的 action 和 啟動的 mTopData
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);
}
return intent;
}
getHomeIntent() 擷取到的就是 Launcher.java 啟動 Intent
//android-8.0.0_r1/packages/apps/Launcher3/AndroidManifest.xml
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
....
1. 手機啟動的首頁面
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan|stateUnchanged"
android:screenOrientation="nosensor"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
2. 設定了 android.intent.action.MAIN 屬性就是主啟動
<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>
...
</manifest>
這裡看到清單檔案中 Launcher 中 intent-filter action配置android.intent.action.MAIN 屬性,這樣Launcher 的 Activity 也就成了主 Activity
接着上述代碼中 ActivityManagerService中startHomeActivityLocked函數标記4處
調用 startActivityLocked 來啟動 Launcher Activity
//com.android.server.am; ActivityStarter.java
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
1 Launcher 的 onCreate 函數
mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
null /*container*/, null /*inTask*/, "startHomeActivity: " + reason);
if (mSupervisor.inResumeTopActivity) {
mSupervisor.scheduleResumeTopActivities();
}
}
// package com.android.launcher3; Launcher.java
public class Launcher extends BaseActivity implements LauncherExterns, View.OnClickListener, OnLongClickListener,LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener, AccessibilityManager.AccessibilityStateChangeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
1 加載布局 R.layout.launcher
mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
...
2 設定布局底層解析
setContentView(mLauncherView);
...
}
}
至此startActivityLocked 來啟動 Launcher Activity,Activity 啟動,終進入到 Launcher 的 onCreate 生命周期函數中.
Launcher 應用圖示顯示
Launcher onCreate 生命周期函數
//com.android.launcher3; Launcher.java
public class Launcher extends BaseActivity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
AccessibilityManager.AccessibilityStateChangeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
super.onCreate(savedInstanceState);
1 擷取 LauncherAppState 執行個體
LauncherAppState app = LauncherAppState.getInstance(this);
2 将 Launcer 與 LauncherAppState 對象綁定
mModel = app.setLauncher(this);
...
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
mPaused = false;
mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
.....
lockAllApps();
3 調用 LauncherModel 的 startLoader 函數
if (!mModel.startLoader(currentScreen)) {
mDragLayer.setAlpha(0);
} else {
...
}
setContentView(mLauncherView);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
}
我們先來看 LauncherAppState 的 setLauncher 方法實作
//com.android.launcher3; LauncherAppState.java
LauncherModel setLauncher(Launcher launcher) {
getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
LauncherModel initialize 初始化工作
mModel.initialize(launcher);
return mModel;
}
LauncherModel initialize 具體實作
//com.android.launcher3; LauncherModel.java
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
Preconditions.assertUIThread();
// Remove any queued UI runnables
mHandler.cancelAll();
1 Callbacks ,也就是傳入的 Launcher
mCallbacks = new WeakReference<>(callbacks);
}
}
我們得知 mCallBack 的成員變量指的就是封裝成弱引用對象 Launcher 這個mCallback會在Launcher 的onCreate 3 處調用 ,我們看注釋 3 的 LauncherModel 調用 startLoader 函數實作:
//com.android.launcher3; LauncherModel.java
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
1. 建立了消息循環的線程
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
//啟動
sWorkerThread.start();
}
2.建立 Hander并且傳入HanderThread的Looper
@Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
//Launcher 的弱引用
@Thunk WeakReference<Callbacks> mCallbacks;
public boolean startLoader(int synchronousBindPage) {
InstallShortcutReceiver.enableInstallQueue();
synchronized (mLock) {
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
runOnMainThread(new Runnable() {
public void run() {
oldCallbacks.clearPendingBinds();
}
});
// If there is already one running, tell it to stop.
stopLoaderLocked();
3. 建立 LoaderTask
mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
&& mModelLoaded && !mIsLoaderTaskRunning) {
mLoaderTask.runBindSynchronousPage(synchronousBindPage);
return true;
} else {
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
4. 将mLoaderTask 作為消息發送給 HandlerThread
sWorker.post(mLoaderTask);
}
}
}
return false;
}
...//省略部分代碼
}
由上LauncherModel可知做了如下操作
1 建立具有消息循環的線程HanderThread的對象
2 建立Hander并且傳入了HandlerThread的Looper,Hander向HandlerThread發消息
3 建立 LoaderTask 執行個體,它實作了 Runnable 接口
4 LoaderTask 作為消息發送給了 HandlerThread 的 Looper 來處理,最後 在 LoaderTask 的 run 函數回調
看下 LoaderTask run的 方法
private class LoaderTask implements Runnable {
...
public void run() {
synchronized (mLock) {
if (mStopped) {
return;
}
mIsLoaderTaskRunning = true;
}
try {
if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
// Set to false in bindWorkspace()
mIsLoadingAndBindingWorkspace = true;
1. 加載工作區資訊
loadWorkspace();
2. 綁定工作區資訊
bindWorkspace(mPageToBindFirst);
...
3. 加載系統以及安裝額應用程式資訊
loadAllApps();
...
} catch (CancellationException e) {
}
...
}
Launcher 是工作區的形式來顯示系統安裝的應用程式的快捷圖示的,每一個工作區都是用來描述一個抽象桌面的,它有 n 個螢幕組成,每一個螢幕又分為 n 個單元格,每個單元格用來顯示一個應用程式的快捷圖示。在 1 、2 處分别調用了 bindWorkspace 函數來加載和綁定工作區資訊。
我們看下3處的實作
//com.android.launcher3 LauncherModel.java
private void loadAllApps() {
...
mHandler.post(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
1.callbacks 實際指向的 Launcher 的,
callbacks.bindAllApplications(added);
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - bindTime) + "ms");
}
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
}
}
});
...
}
在一處 callbacks 實際指向的 Launcher 的,是以 Launcher.java 類中看它具體實作 bindAllApplications
//com.android.launcher3; Launcher.java
public void bindAllApplications(final ArrayList<AppInfo> apps) {
if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
mTmpAppsList = apps;
return;
}
if (mAppsView != null) {
1. 将包含應用資訊的清單 apps 傳入 mAppsView 中
mAppsView.setApps(apps);
}
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
//com.android.launcher3.allapps; AllAppsContainerView.java
public void setApps(List<AppInfo> apps) {
mApps.setApps(apps);
}
mApps 成員變量是 AlphabeticalAppsList 裡面的函數,接着在跟
//com.android.launcher3.allapps; AlphabeticalAppsList.java
public void setApps(List<AppInfo> apps) {
mComponentToAppMap.clear();
1. 添加 APP
addApps(apps);
}
public void addApps(List<AppInfo> apps) {
2. 更新 APP
updateApps(apps);
}
/**
* Updates existing apps in the list
*/
public void updateApps(List<AppInfo> apps) {
for (AppInfo app : apps) {
mComponentToAppMap.put(app.toComponentKey(), app);
}
onAppsUpdated();
}
private void updateAdapterItems() {
refillAdapterItems();
refreshRecyclerView();
}
private void refreshRecyclerView() {
if (mAdapter != null) {
//重新整理 RecyclerView.Adapter
mAdapter.notifyDataSetChanged();
}
}
}
我們在看初始化Adapter的地方在 就在 AllAppsContainerView 布局對象加載完成之後的回調 onFinishInflate 中
//com.android.launcher3.allapps; AllAppsContainerView.java
@Override
protected void onFinishInflate() {
super.onFinishInflate();
....
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);
mAppsRecyclerView.setLayoutManager(mLayoutManager);
得到RecyclerView并且設定加載擴充卡
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
mAppsRecyclerView.addOnScrollListener(mElevationController);
mAppsRecyclerView.setElevationController(mElevationController);
}
launcher 啟動流程分析完了,我們來總結一下
Android系統啟動流程總結
1 啟動電源及系統啟動。當電源按下時引導晶片代碼從預定義的地方(固化在ROM)開始執行。加載引導程式BootLoader到RAM,然後執行;
2 引導程式BootLoader。引導程式BootLoader是在Android作業系統開始運作前的一個小程式,它的主要作用是把OS拉起來并運作;
3 Linux核心啟動。當核心啟動時,設定緩存、被保護存儲器、計劃清單、加載驅動。當核心完成系統設定時,它首先在系統檔案中尋找init.rc檔案,并啟動init程序;
4 init程序啟動。初始化合啟動屬性服務,并且啟動Zygote程序;
5 Zygote程序啟動。建立Java虛拟機并為Java虛拟機注冊JNI方法,建立服務端Socket,啟動SystemServer程序;
6 SystemServer程序啟動。啟動Binder線程池和SystemServiceManager,并且啟動各種系統服務;
7 Launcher啟動。被SystemServer程序啟動的AMS會啟動Launcher,Launcher啟動後會将已安裝應用的快捷圖示顯示到界面上。