English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
In the past, we analyzed Android6The process of installing applications during the boot of .0 system, after these applications are installed, the Launcher application is responsible for displaying them on the desktop.
I. Starting Launcher by AMS
The Launcher application is started directly by calling startHomeActivityLocked in the AMS's systemReady method, and the following code shows how systemReady starts the Launcher.
startHomeActivityLocked(mCurrentUserId, "systemReady");Let's examine this function, which first calls the getHomeIntent method to obtain Intent, then calls the resolveActivityInfo function to obtain ActivityInfo from PKMS, and finally calls the startHomeActivity function of ActivityStackSupervisor when the process has not started.
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; } Intent intent = getHomeIntent();//Get intent ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//Get ActivityInfo 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) {//Process did not start call EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mStackSupervisor.startHomeActivity(intent, aInfo, reason); } } return true; }
Let's first take a look at the getHomeIntent function.
Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null63; Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; }
Then let's take a look at the startHomeActivity function of ActivityStackSupervisor, which also calls startActivityLocked to start an Activity. This function has been analyzed in previous blog posts, so we will not introduce it here.
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason); startActivityLocked(null) /* caller */, intent, null /* resolvedType */, aInfo, 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 */, null /* outActivity */, null /* container */, null /* inTask */; if (inResumeTopActivity) { // If we are in the resume section already, the home activity will be initialized, but not // resumed (to avoid recursive resume) and will stay that way until something pokes it // again. We need to schedule another resume. scheduleResumeTopActivities(); } }
Second, Launcher startup
Let's take a look at the AndroidManifest.xml of Launcher, we can see that its main Activity has a category of android.intent.category.HOME
<application android:name="com.android.launcher2.LauncherApplication" android:label="@string/application_name" android:icon="@mipmap/ic_launcher_home" android:hardwareAccelerated="true" android:largeHeap="@bool/config_largeHeap" android:supportsRtl="true"> <activity android:name="com.android.launcher2.Launcher" android:launchMode="singleTask"> android:clearTaskOnLaunch="true"> android:stateNotNeeded="true"> android:resumeWhilePausing="true"> android:theme="@style/Theme android:windowSoftInputMode="adjustPan"> android:screenOrientation="nosensor"> <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> ......
The mModel.startLoader function is called in the onCreate function of Launcher.java
protected void onCreate(Bundle savedInstanceState) { ...... if (!mRestoring) { if (sPausedFromUserAction) { // If the user leaves the launcher, we should just load items asynchronously when // they return. mModel.startLoader(true, -1; } // We only load the page synchronously if the user rotates (or triggers a // while the launcher is in the foreground mModel.startLoader(true, mWorkspace.getCurrentPage()); } } ......
The startLoader function will post a Runnable message, let's take a look at its run method
public void startLoader(boolean isLaunching, int synchronousBindPage) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // Clear any deferred bind-runnables from the synchronized load process // We must do this before any loading/binding is scheduled below. 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. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp, isLaunching); if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }
In its run method, it will call the loadAndBindAllApps function, and in the loadAndBindAllApps function, it will call the loadAllAppsByBatch function
public void run() { synchronized (mLock) { mIsLoaderTaskRunning = true; } final Callbacks cbk = mCallbacks.get() final boolean loadWorkspaceFirst = cbk != null63; (!cbk.isAllAppsVisible()) : true; keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } // First step. Load workspace first, this is necessary since adding of apps from // managed profile in all apps is deferred until onResume. See http://b/17336902. if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step" 1: loading workspace); loadAndBindWorkspace(); } Log.d(TAG, "step" 1: special: loading all apps); loadAndBindAllApps(); }
Let's take a look at the loadAndBindAllApps function, which first enters a while loop, then calls the LauncherApps' getActivityList function, and then calls callbacks' bindAllApplications function.
private void loadAllAppsByBatch() {}} final long t = DEBUG_LOADERS &63; SystemClock.uptimeMillis() : 0; ...... mBgAllAppsList.clear(); final int profileCount = profiles.size(); for (int p = 0; p < profileCount; p++) { ...... while (i < N && !mStopped) { if (i == 0) { final long qiaTime = DEBUG_LOADERS &63; SystemClock.uptimeMillis() : 0; apps = mLauncherApps.getActivityList(null, user); ...... mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (firstProfile) { callbacks.bindAllApplications(added); } callbacks.bindAppsAdded(added); } if (DEBUG_LOADERS) { Log.d(TAG, "bound ") + added.size() + " apps in " + (SystemClock.uptimeMillis() - t) + "ms"); } } Log.i(TAG, "not binding apps: no Launcher activity"); } } }); ......
Let's first look at the getActivityCreatedList function of LauncherApps, which first calls the getLauncherActivities function using the mService member variable to obtain a list<ResolveInfo>, and then wraps it in an ArrayList<LauncherActivityInfo>.
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { List<ResolveInfo> activities = null; try { activities = mService.getLauncherActivities(packageName, user); } catch (RemoteException re) { throw new RuntimeException("Failed to call LauncherAppsService"); } if (activities == null) { return Collections.EMPTY_LIST; } ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>(); final int count = activities.size(); for (int i = 0; i < count; i++) { ResolveInfo ri = activities.get(i); long firstInstallTime = 0; try { firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime; } catch (NameNotFoundException nnfe) { // Sorry, can't find package } LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user, firstInstallTime); if (DEBUG) { Log.v(TAG, "Returning activity for profile ", + user + ": " + lai.getComponentName()); } lais.add(lai); } return lais; }
Its service is class LauncherAppsImpl extends ILauncherApps.Stub, followed by the getLauncherActivities function, which is definitely used to obtain the ResolveInfo of related Activity through PKMS.
@Override public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); if (!isUserEnabled(user)) { return new ArrayList<ResolveInfo>(); } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mainIntent.setPackage(packageName); long ident = Binder.clearCallingIdentity(); try { List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */, return apps; finally { } Binder.restoreCallingIdentity(ident); } }
Finally, call the bindAllApplications function in Launcher.java, and in this function, you can display all the applications in the system on the desktop.
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { Runnable setAllAppsRunnable = new Runnable() { public void run() { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); } } ; // Remove the progress bar entirely; we could also make it disappear // but it is better to remove it since we know it is not going to be used View progressBar = mAppsCustomizeTabHost. findViewById(R.id.apps_customize_progress_bar); if (progressBar != null) { ((ViewGroup)progressBar.getParent()).removeView(progressBar); // We just post the call to setApps so that the user sees the progress bar // disappear-- otherwise, it just looks like the progress bar has frozen // which does not look good mAppsCustomizeTabHost.post(setAllAppsRunnable); } // If we did not initialize the spinner in onCreate, then we can directly set the // the list of applications without waiting for any progress bars or views to be hidden. setAllAppsRunnable.run(); } }
III. Display application icons
Let's take a look at the onClick function of the Launcher, when calling showWorkspace, it can display the icons of all applications.
public void onClick(View v) { // Make sure that rogue clicks do not get through while all apps are launching, or after the // the view has been detached (it is possible for this to happen if the view is removed during a touch event). if (v.getWindowToken() == null) { return; } if (!mWorkspace.isFinishedSwitchingState()) { return; } 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())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; mWaitingForResume.setStayPressed(true); } } if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; handleFolderClick(fi); } } if (isAllAppsVisible()) { showWorkspace(true); } onClickAllAppsButton(v); } } }
All icons will be displayed in showWorkspace;
void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE) { boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED); mWorkspace.setVisibility(View.VISIBLE); hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable); // Show the search bar (only animate if we were showing the drop target bar in spring // loaded mode) if (mSearchDropTargetBar != null) { mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode); } // We only need to animate in the dock divider if we're going from spring loaded mode showDockDivider(animated && wasInSpringLoadedMode); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { mAllAppsButton.requestFocus(); } } mWorkspace.flashScrollingIndicator(animated); // Change the state *after* we've called all the transition code mState = State.WORKSPACE; // Resume the auto-advance of widgets mUserPresent = true; updateRunning(); // Send an accessibility event to announce the context change getWindow().getDecorView() .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); }
And clicking the application icon will eventually call the startActivitySafely in Launcher.java to start the application. The startActivity called here is the Activity's startActivity function.
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; }
That is all for the content of this article, I hope it will be helpful to everyone's learning, and I also hope that everyone will support the Yelling Tutorial more.
Statement: The content of this article is from the Internet, the copyright belongs to the original author, the content is contributed and uploaded by Internet users spontaneously, this website does not own the copyright, has not been edited by humans, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)