Welcome

首页 / 移动开发 / Android / Android6.0 Launcher2应用解析

在之前我们分析了Android6.0系统在启动时安装应用程序的过程,这些应用程序安装好之后,Launcher应用就负责把它们在桌面上展示出来。

一、AMS启动Launcher 

Launcher应用是在AMS的systemReady方法中直接调用startHomeActivityLocked启动的,下面是systemReady启动Launcher的代码。 

startHomeActivityLocked(mCurrentUserId, "systemReady");我们来看下这个函数,先调用了getHomeIntent方法来获取Intent,然后也是调用resolveActivityInfo函数从PKMS获取ActivityInfo,接着当进程没有启动的话,调用ActivityStackSupervisor的startHomeActivity函数

 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();//获取intentActivityInfo aInfo =resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//获取ActivityInfoif (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) {//进程没有启动调用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;} 
我们先来看看getHomeIntent这个函数。

Intent getHomeIntent() {Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);intent.setComponent(mTopComponent);if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {intent.addCategory(Intent.CATEGORY_HOME);}return intent;}
然后我们来看下ActivityStackSupervisor的startHomeActivity函数,它也是调用了startActivityLocked来启动Activity的,在之前的博客分析过这个函数这里我们就不介绍了。

 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 resume section already, 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();}}
二、Launcher启动 

接着我们来看下Launcher的AndroidManifest.xml,我们看下其主Activity有一个category为android.intent.category.HOME 

<applicationandroid: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"><activityandroid: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>...... 
在Launcher.java的onCreate函数中调用了mModel.startLoader函数

 protected void onCreate(Bundle savedInstanceState) {......if (!mRestoring) {if (sPausedFromUserAction) {// If the user leaves launcher, then we should just load items asynchronously when// they return.mModel.startLoader(true, -1);} else {// We only load the page synchronously if the user rotates (or triggers a// configuration change) while launcher is in the foregroundmModel.startLoader(true, mWorkspace.getCurrentPage());}}...... 
startLoader函数会post一个Runnable消息,我们来看下它的run方法 

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 anythingif (mCallbacks != null && mCallbacks.get() != null) {// If there is already one running, tell it to stop.// also, don"t downgrade isLaunching if we"re already runningisLaunching = isLaunching || stopLoaderLocked();mLoaderTask = new LoaderTask(mApp, isLaunching);if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {mLoaderTask.runBindSynchronousPage(synchronousBindPage);} else {sWorkerThread.setPriority(Thread.NORM_PRIORITY);sWorker.post(mLoaderTask);}}}}
 在它的run方法中会调用loadAndBindAllApps函数,在loadAndBindAllApps函数中又会调用loadAllAppsByBatch函数 

public void run() {synchronized (mLock) {mIsLoaderTaskRunning = true;}final Callbacks cbk = mCallbacks.get();final boolean loadWorkspaceFirst = cbk != null ? (!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();} else {Log.d(TAG, "step 1: special: loading all apps");loadAndBindAllApps();} 
我们先来看下loadAndBindAllApps函数,这个函数先进入while循环,然后调用了LauncherApps的getActivityList函数,后面又会调用callbacks的bindAllApplications

private void loadAllAppsByBatch() {final long t = DEBUG_LOADERS ? 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 ? 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);} else {callbacks.bindAppsAdded(added);}if (DEBUG_LOADERS) {Log.d(TAG, "bound " + added.size() + " apps in "+ (SystemClock.uptimeMillis() - t) + "ms");}} else {Log.i(TAG, "not binding apps: no Launcher activity");}}});......
我们先来看LauncherApps的getActivityList函数,它先用mService成员变量调用getLauncherActivities函数获取到list<ResolveInfo>,然后封装在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;} 
其service是class LauncherAppsImpl extends ILauncherApps.Stub 下面是getLauncherActivities函数,肯定也是通过PKMS来获取相关Activity的ResolveInfo的。 

@Overridepublic 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 */,user.getIdentifier());return apps;} finally {Binder.restoreCallingIdentity(ident);}} 
最后回调Launcher.java的bindAllApplications函数,最后在这个函数中可以在桌面上展示系统中所有的应用程序了。

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 GONE// but better to remove it since we know it"s not going to be usedView 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 the user sees the progress bar// disappear-- otherwise, it just looks like the progress bar froze// which doesn"t look greatmAppsCustomizeTabHost.post(setAllAppsRunnable);} else {// If we did not initialize the spinner in onCreate, then we can directly set the// list of applications without waiting for any progress bars views to be hidden.setAllAppsRunnable.run();}}
三、显示应用图标 
我们再来看下Launcher的onClick函数,当调用showWorkspace可以显示所有应用的图标。

 public void onClick(View v) {// Make sure that rogue clicks don"t get through while allapps is launching, or after the// view has detached (it"s possible for this to happen if the view is removed mid touch).if (v.getWindowToken() == null) {return;}if (!mWorkspace.isFinishedSwitchingState()) {return;}Object tag = v.getTag();if (tag instanceof ShortcutInfo) {// Open shortcutfinal 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);}} else if (tag instanceof FolderInfo) {if (v instanceof FolderIcon) {FolderIcon fi = (FolderIcon) v;handleFolderClick(fi);}} else if (v == mAllAppsButton) {if (isAllAppsVisible()) {showWorkspace(true);} else {onClickAllAppsButton(v);}}} 
在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 modeshowDockDivider(animated && wasInSpringLoadedMode);// Set focus to the AppsCustomize buttonif (mAllAppsButton != null) {mAllAppsButton.requestFocus();}}mWorkspace.flashScrollingIndicator(animated);// Change the state *after* we"ve called all the transition codemState = State.WORKSPACE;// Resume the auto-advance of widgetsmUserPresent = true;updateRunning();// Send an accessibility event to announce the context changegetWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);} 
而点击应用图标,最终会调用Launcher.java的startActivitySafely来启动应用。这里调用的startActivity就是Activity的startActivity函数。 
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;}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。