In this article, we will learn the final step of the Android system startup process: the startup process of the Launcher.
1. The description of the Launcher
The final step in Android startup is to launch a Home application that displays the applications already installed on the system. This Home application is called the Launcher. During the startup process of the application Launcher, the PackageManagerService is asked to return information about installed applications in the system, and this information is encapsulated into a list of shortcut ICONS displayed on the system screen. In this way, users can click these shortcut ICONS to start the corresponding applications.
2.Launcher process
When the SyetemServer process is started, PackageManagerService is started. After the PackageManagerService is started, applications in the system are installed. The ActivityManagerService that you started earlier launches the Launcher.
The entry to start the Launcher is ActivityManagerService#systemReady, as shown below. frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {... mActivityManagerService.systemReady(newRunnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready"); mSystemServiceManager.startBootPhase( SystemService.PHASE_ACTIVITY_MANAGER_READY); . }... }Copy the code
In the startOtherServices function, ActivityManagerService#systemReady is called: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback) { ... synchronized (this) { ... mStackSupervisor.resumeFocusedStackTopActivityLocked(); mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId); }}Copy the code
SystemReady function calls in the ActivityStackSupervisor# resumeFocusedStackTopActivityLocked:
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java**
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if(targetStack ! = null && isFocusedStack(targetStack)) {returntargetStack.resumeTopActivityUncheckedLocked(target, targetOptions); //1 } final ActivityRecord r = mFocusedStack.topRunningActivityLocked();if(r == null || r.state ! = RESUMED) { mFocusedStack.resumeTopActivityUncheckedLocked(null, null); }return false;
}
Copy the code
In the note 1 invokes ActivityStack# resumeTopActivityUncheckedLocked, ActivityStack object is used to describe the Activity of the stack, as shown below.
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); //1 } finally { mStackSupervisor.inResumeTopActivity = false; } return result; }Copy the code
Note 1 call the resumeTopActivityInnerLocked function:
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "prevFinished"); . }Copy the code
ResumeTopActivityInnerLocked function code is very long, we capture what we want to analysis the key sentence: call ActivityStackSupervisor# resumeHomeStackTask, code as shown below. frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) {
...
if(r ! = null && ! r.finishing) { mService.setFocusedActivityLocked(r, myReason);return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
returnmService.startHomeActivityLocked(mCurrentUser, myReason); / / 1}Copy the code
ActivityManagerService#startHomeActivityLocked is called at comment 1, as shown below. 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) {//1
return false; } Intent intent = getHomeIntent(); //2 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.instrumentationClass == null) {//3 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mActivityStarter.startHomeActivityLocked(intent, aInfo, reason); / / 4}}else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
Copy the code
The mFactoryTest in comment 1 represents the operating mode of the system. There are three operating modes: non-factory mode, low-level factory mode, and advanced factory mode. MTopAction describes the Action of the first Activity component to be started. Its value is intent.action_main. So the code in comment 1 means that if mFactoryTest is factorytest.factory_test_low_level and mTopAction=null, return false. The getHomeIntent function at comment 2 is shown below.
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); }return intent;
}
Copy the code
The getHomeIntent function creates the Intent and passes in mTopAction and mTopData. The value of mTopAction is intent.action_main, and if the system is not running in low-level factory mode, set the Category of the Intent to intent.category_HOME. ACTION_MAIN = intent.action_main = intent.action_main = intent.action_main = intent.action_main = intent.action_main = intent.action_main = intent.action_main Whether the application with Category intent.category_HOME has been started. If not, call the method in Comment 4 to start the application. The application that is launched is the Launcher, because the intent-filter tag in the Manifest file of the Launcher matches the Action as intent.action_main and the Category as intent.category_HOME. The Manifest file for the Launcher looks like this. 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="16"/>
...
<application
...
<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>
Copy the code
The application Launcher is launched and its onCreate function is executed.
3. Application icon display process in Launcher
The onCreate function of the Launcher looks like this. packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override protected void onCreate(Bundle savedInstanceState) { ... LauncherAppState app = LauncherAppState.getInstance(); //1 mDeviceProfile = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? app.getInvariantDeviceProfile().landscapeProfile : app.getInvariantDeviceProfile().portraitProfile; mSharedPrefs = Utilities.getPrefs(this); mIsSafeModeEnabled = getPackageManager().isSafeMode(); mModel = app.setLauncher(this); / / 2...if(! mRestoring) {if(DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) { mModel.startLoader(PagedView.INVALID_RESTORE_PAGE); / / 2}else{ mModel.startLoader(mWorkspace.getRestorePage()); }}... }Copy the code
Get the instance of LauncherAppState at comment 1 and call its setLauncher function at comment 2 and pass in the Launcher object, LauncherAppState#setLauncher, as shown below. packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java
LauncherModel setLauncher(Launcher launcher) { getLauncherProvider().setLauncherProviderChangeListener(launcher); mModel.initialize(launcher); //1 mAccessibilityDelegate = ((launcher ! = null) && Utilities.ATLEAST_LOLLIPOP) ? new LauncherAccessibilityDelegate(launcher) : null;return mModel;
}
Copy the code
The Initialize function of the LauncherModel is called in comment 1:
public void initialize(Callbacks callbacks) { synchronized (mLock) { unbindItemInfosAndClearQueuedBindRunnables(); mCallbacks = new WeakReference<Callbacks>(callbacks); }}Copy the code
The Initialize function wraps the Callbacks, the incoming Launcher, into a weak reference object. So we know that the mCallbacks variable refers to the Launcher wrapped as a weak reference object, which will be used later in this mCallbacks article. Back to the onCreate function of the Launcher, we call LauncherModel#startLoader in comment 2: packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java
. @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); //1 static { sWorkerThread.start(); } @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper()); / / 2... public void startLoader(int synchronousBindPage, int loadFlags) {s InstallShortcutReceiver.enableInstallQueue(); synchronized (mLock) { synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.clear(); }if(mCallbacks ! = null && mCallbacks.get() ! = null) { stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags); / / 3if(synchronousBindPage ! = PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded && mWorkspaceLoaded && ! mIsLoaderTaskRunning) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); }else{ sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); //4}}}}Copy the code
Note 1 creates a thread HandlerThread object with a message loop. Handler is created at comment 2 and Looper for the HandlerThread is passed in. The role of the Hander is to send a message to the HandlerThread. Create a LoaderTask at comment 3 and send the LoaderTask as a message to the HandlerThread at comment 4. The LoaderTask class implements the Runnable interface, and its run function is called when the message described by LoaderTask is processed, as shown below
private class LoaderTask implements Runnable {
...
public void run() {
synchronized (mLock) {
if (mStopped) {
return;
}
mIsLoaderTaskRunning = true;
}
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); loadAndBindWorkspace(); / / 1if (mStopped) {
break keep_running;
}
waitForIdle();
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); //2 } mContext = null; synchronized (mLock) {if (mLoaderTask == this) {
mLoaderTask = null;
}
mIsLoaderTaskRunning = false;
mHasLoaderCompletedOnce = true; }}... }Copy the code
Each workspace describes an abstract desktop. It consists of N screens. Each screen is divided into N cells, and each cell is used to display a shortcut icon for an application. Note 1 calls the loadAndBindWorkspace function to load workspace information, and note 2 calls the loadAndBindAllApps function to load information about applications that have been installed on the system. The code of the loadAndBindAllApps function is shown below.
private void loadAndBindAllApps() {
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
}
if(! mAllAppsLoaded) { loadAllApps(); //1 synchronized (LoaderTask.this) {if (mStopped) {
return;
}
}
updateIconCache();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
mAllAppsLoaded = true; }}else{ onlyBindAllApps(); }}Copy the code
If the system does not load the installed application information, the loadAllApps function at comment 1 is called:
private void loadAllApps() {... mHandler.post(newRunnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if(callbacks ! = null) { callbacks.bindAllApplications(added); / / 1if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
+ (SystemClock.uptimeMillis() - bindTime) + "ms"); }}else {
Log.i(TAG, "not binding apps: no Launcher activity"); }}}); . }Copy the code
The bindAllApplications function of the callbacks is called in comment 1. We learned earlier that the callbacks are actually pointing to the Launcher, so let’s look at launch #bindAllApplications, as shown below. 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) { mAppsView.setApps(apps); / / 1}if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
Copy the code
At comment 1, AllAppsContainerView#setApps is called and a list of apps containing the application information is passed in.
packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java AllAppsContainerView#setApps
public void setApps(List<AppInfo> apps) {
mApps.setApps(apps);
}
Copy the code
The list of apps that contains the application information is already passed to AllAppsContainerView. Look at the AllAppsContainerView#onFinishInflate function:
@Override
protected void onFinishInflate() { super.onFinishInflate(); . // Load the all apps recycler view mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view); //1 mAppsRecyclerView.setApps(mApps); //2 mAppsRecyclerView.setLayoutManager(mLayoutManager); mAppsRecyclerView.setAdapter(mAdapter); //3 mAppsRecyclerView.setHasFixedSize(true); mAppsRecyclerView.addOnScrollListener(mElevationController); mAppsRecyclerView.setElevationController(mElevationController); . }Copy the code
The onFinishInflate function is invoked when the XML file is loaded, and you get AllAppsRecyclerView at comment 1 to display the list of apps, and pass in the list of apps at comment 2. And set the Adapter for AllAppsRecyclerView in comment 3. The list of application shortcut ICONS is displayed on the screen. Here’s the startup process of the Android system.
4.Android startup process
Combined with this and the previous three articles in this series, we can figure out the Android startup process as follows.
- Boot power and system boot When the power is pressed boot chip code starts executing from a predefined location (solidified in ROM). Load the boot program Bootloader into RAM and execute.
- BootLoader BootLoader is a small program before the Android operating system starts to run. Its main function is to pull up the operating system and run it.
- Linux kernel startup When the kernel starts, set the cache, protected storage, schedule list, and load drivers. When the kernel is done setting up the system, it first looks for the init.rc file in the system files and starts the init process.
- The init process initializes and starts the properties service, and starts the Zygote process.
- Zygote creates JavaVM, registers JNI for JavaVM, creates server Socket, and starts SystemServer.
- The SystemServer process starts the Binder thread pool and SystemServiceManager, and starts various system services.
- The ActivityManagerService started by the SystemServer process starts the Launcher. After the Launcher is started, the shortcut icon of the installed application is displayed on the screen.
Combined with the above process: