Android Launcher2启动分析


一、AMS启动Launcher

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

  1. startHomeActivityLocked(mCurrentUserId, "systemReady");  
我们来看下这个函数,先调用了getHomeIntent方法来获取Intent,然后也是调用resolveActivityInfo函数从PKMS获取ActivityInfo,接着当进程没有启动的话,调用ActivityStackSupervisor的startHomeActivity函数
  1. boolean startHomeActivityLocked(int userId, String reason) {  
  2.     if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL  
  3.             && mTopAction == null) {  
  4.         // We are running in factory test mode, but unable to find  
  5.         // the factory test app, so just sit around displaying the  
  6.         // error message and don't try to start anything.  
  7.         return false;  
  8.     }  
  9.     Intent intent = getHomeIntent();//获取intent  
  10.     ActivityInfo aInfo =  
  11.         resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//获取ActivityInfo  
  12.     if (aInfo != null) {  
  13.         intent.setComponent(new ComponentName(  
  14.                 aInfo.applicationInfo.packageName, aInfo.name));  
  15.         // Don't do this if the home app is currently being  
  16.         // instrumented.  
  17.         aInfo = new ActivityInfo(aInfo);  
  18.         aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);  
  19.         ProcessRecord app = getProcessRecordLocked(aInfo.processName,  
  20.                 aInfo.applicationInfo.uid, true);  
  21.         if (app == null || app.instrumentationClass == null) {//进程没有启动调用  
  22.             EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName);  
  23.             intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);  
  24.             mStackSupervisor.startHomeActivity(intent, aInfo, reason);  
  25.         }  
  26.     }  
  27.   
  28.     return true;  
  29. }  

我们先来看看getHomeIntent这个函数。

  1. Intent getHomeIntent() {  
  2.     Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);  
  3.     intent.setComponent(mTopComponent);  
  4.     if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {  
  5.         intent.addCategory(Intent.CATEGORY_HOME);  
  6.     }  
  7.     return intent;  
  8. }  
然后我们来看下ActivityStackSupervisor的startHomeActivity函数,它也是调用了startActivityLocked来启动Activity的,在之前的博客分析过这个函数这里我们就不介绍了。
  1. void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {  
  2.     moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);  
  3.     startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,  
  4.             null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,  
  5.             null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,  
  6.             null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,  
  7.             0 /* startFlags */, null /* options */false /* ignoreTargetSecurity */,  
  8.             false /* componentSpecified */,  
  9.             null /* outActivity */, null /* container */,  null /* inTask */);  
  10.     if (inResumeTopActivity) {  
  11.         // If we are in resume section already, home activity will be initialized, but not  
  12.         // resumed (to avoid recursive resume) and will stay that way until something pokes it  
  13.         // again. We need to schedule another resume.  
  14.         scheduleResumeTopActivities();  
  15.     }  
  16. }  


二、Launcher启动

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

  1. <application  
  2.     android:name="com.android.launcher2.LauncherApplication"  
  3.     android:label="@string/application_name"  
  4.     android:icon="@mipmap/ic_launcher_home"  
  5.     android:hardwareAccelerated="true"  
  6.     android:largeHeap="@bool/config_largeHeap"  
  7.     android:supportsRtl="true">  
  8.     <activity  
  9.         android:name="com.android.launcher2.Launcher"  
  10.         android:launchMode="singleTask"  
  11.         android:clearTaskOnLaunch="true"  
  12.         android:stateNotNeeded="true"  
  13.         android:resumeWhilePausing="true"  
  14.         android:theme="@style/Theme"  
  15.         android:windowSoftInputMode="adjustPan"  
  16.         android:screenOrientation="nosensor">   
  17.         <intent-filter>  
  18.             <action android:name="android.intent.action.MAIN" />  
  19.             <category android:name="android.intent.category.HOME" />  
  20.             <category android:name="android.intent.category.DEFAULT" />  
  21.             <category android:name="android.intent.category.MONKEY"/>  
  22.         </intent-filter>  
  23.     </activity>  
  24.     ......  

在Launcher.Java的onCreate函数中调用了mModel.startLoader函数

  1. protected void onCreate(Bundle savedInstanceState) {  
  2.     ......  
  3.     if (!mRestoring) {  
  4.         if (sPausedFromUserAction) {  
  5.             // If the user leaves launcher, then we should just load items asynchronously when  
  6.             // they return.  
  7.             mModel.startLoader(true, -1);  
  8.         } else {  
  9.             // We only load the page synchronously if the user rotates (or triggers a  
  10.             // configuration change) while launcher is in the foreground  
  11.             mModel.startLoader(true, mWorkspace.getCurrentPage());  
  12.         }  
  13.     }  
  14.     ......  

startLoader函数会post一个Runnable消息,我们来看下它的run方法

  1. public void startLoader(boolean isLaunching, int synchronousBindPage) {  
  2.     synchronized (mLock) {  
  3.         if (DEBUG_LOADERS) {  
  4.             Log.d(TAG, "startLoader isLaunching=" + isLaunching);  
  5.         }  
  6.   
  7.         // Clear any deferred bind-runnables from the synchronized load process  
  8.         // We must do this before any loading/binding is scheduled below.  
  9.         mDeferredBindRunnables.clear();  
  10.   
  11.         // Don't bother to start the thread if we know it's not going to do anything  
  12.         if (mCallbacks != null && mCallbacks.get() != null) {  
  13.             // If there is already one running, tell it to stop.  
  14.             // also, don't downgrade isLaunching if we're already running  
  15.             isLaunching = isLaunching || stopLoaderLocked();  
  16.             mLoaderTask = new LoaderTask(mApp, isLaunching);  
  17.             if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {  
  18.                 mLoaderTask.runBindSynchronousPage(synchronousBindPage);  
  19.             } else {  
  20.                 sWorkerThread.setPriority(Thread.NORM_PRIORITY);  
  21.                 sWorker.post(mLoaderTask);  
  22.             }  
  23.         }  
  24.     }  
  25. }  

在它的run方法中会调用loadAndBindAllApps函数,在loadAndBindAllApps函数中又会调用loadAllAppsByBatch函数

  1. public void run() {  
  2.     synchronized (mLock) {  
  3.         mIsLoaderTaskRunning = true;  
  4.     }  
  5.   
  6.    final Callbacks cbk = mCallbacks.get();  
  7.    final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;  
  8.   
  9.     keep_running: {  
  10.         // Elevate priority when Home launches for the first time to avoid  
  11.         // starving at boot time. Staring at a blank home is not cool.  
  12.         synchronized (mLock) {  
  13.             if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +  
  14.                     (mIsLaunching ? "DEFAULT" : "BACKGROUND"));  
  15.             Process.setThreadPriority(mIsLaunching  
  16.                     ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);  
  17.         }  
  18.   
  19.         // First step. Load workspace first, this is necessary since adding of apps from  
  20.         // managed profile in all apps is deferred until onResume. See http://b/17336902.  
  21.         if (loadWorkspaceFirst) {  
  22.             if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");  
  23.             loadAndBindWorkspace();  
  24.         } else {  
  25.             Log.d(TAG, "step 1: special: loading  all apps");  
  26.             loadAndBindAllApps();  
  27.         }  

我们先来看下loadAndBindAllApps函数,这个函数先进入while循环,然后调用了LauncherApps的getActivityList函数,后面又会调用callbacks的bindAllApplications

  1. private void loadAllAppsByBatch() {  
  2.     final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;  
  3.     ......  
  4.     mBgAllAppsList.clear();  
  5.     final int profileCount = profiles.size();  
  6.     for (int p = 0; p < profileCount; p++) {  
  7.         ......  
  8.         while (i < N && !mStopped) {  
  9.             if (i == 0) {  
  10.                 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;  
  11.                 apps = mLauncherApps.getActivityList(null, user);  
  12.                 ......  
  13.                   
  14.                
  15.             mHandler.post(new Runnable() {  
  16.                 public void run() {  
  17.                     final long t = SystemClock.uptimeMillis();  
  18.                     if (callbacks != null) {  
  19.                         if (firstProfile) {  
  20.                             callbacks.bindAllApplications(added);  
  21.                         } else {  
  22.                             callbacks.bindAppsAdded(added);  
  23.                         }  
  24.                         if (DEBUG_LOADERS) {  
  25.                             Log.d(TAG, "bound " + added.size() + " apps in "  
  26.                                 + (SystemClock.uptimeMillis() - t) + "ms");  
  27.                         }  
  28.                     } else {  
  29.                         Log.i(TAG, "not binding apps: no Launcher activity");  
  30.                     }  
  31.                 }  
  32.             });  
  33.             ......  

我们先来看LauncherApps的getActivityList函数,它先用mService成员变量调用getLauncherActivities函数获取到list<ResolveInfo>,然后封装在ArrayList<LauncherActivityInfo> 中。

  1. public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {  
  2.     List<ResolveInfo> activities = null;  
  3.     try {  
  4.         activities = mService.getLauncherActivities(packageName, user);  
  5.     } catch (RemoteException re) {  
  6.         throw new RuntimeException("Failed to call LauncherAppsService");  
  7.     }  
  8.     if (activities == null) {  
  9.         return Collections.EMPTY_LIST;  
  10.     }  
  11.     ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();  
  12.     final int count = activities.size();  
  13.     for (int i = 0; i < count; i++) {  
  14.         ResolveInfo ri = activities.get(i);  
  15.         long firstInstallTime = 0;  
  16.         try {  
  17.             firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName,  
  18.                 PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime;  
  19.         } catch (NameNotFoundException nnfe) {  
  20.             // Sorry, can't find package  
  21.         }  
  22.         LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,  
  23.                 firstInstallTime);  
  24.         if (DEBUG) {  
  25.             Log.v(TAG, "Returning activity for profile " + user + " : "  
  26.                     + lai.getComponentName());  
  27.         }  
  28.         lais.add(lai);  
  29.     }  
  30.     return lais;  
  31. }  

其service是class LauncherAppsImpl extends ILauncherApps.Stub 下面是getLauncherActivities函数,肯定也是通过PKMS来获取相关Activity的ResolveInfo的。

扫描二维码关注公众号,回复: 181160 查看本文章
  1. @Override  
  2. public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)  
  3.         throws RemoteException {  
  4.     ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);  
  5.     if (!isUserEnabled(user)) {  
  6.         return new ArrayList<ResolveInfo>();  
  7.     }  
  8.   
  9.     final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  10.     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  11.     mainIntent.setPackage(packageName);  
  12.     long ident = Binder.clearCallingIdentity();  
  13.     try {  
  14.         List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,  
  15.                 user.getIdentifier());  
  16.         return apps;  
  17.     } finally {  
  18.         Binder.restoreCallingIdentity(ident);  
  19.     }  
  20. }  

最后回调Launcher.java的bindAllApplications函数,最后在这个函数中可以在桌面上展示系统中所有的应用程序了。

  1. public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {  
  2.     Runnable setAllAppsRunnable = new Runnable() {  
  3.         public void run() {  
  4.             if (mAppsCustomizeContent != null) {  
  5.                 mAppsCustomizeContent.setApps(apps);  
  6.             }  
  7.         }  
  8.     };  
  9.   
  10.     // Remove the progress bar entirely; we could also make it GONE  
  11.     // but better to remove it since we know it's not going to be used  
  12.     View progressBar = mAppsCustomizeTabHost.  
  13.         findViewById(R.id.apps_customize_progress_bar);  
  14.     if (progressBar != null) {  
  15.         ((ViewGroup)progressBar.getParent()).removeView(progressBar);  
  16.   
  17.         // We just post the call to setApps so the user sees the progress bar  
  18.         // disappear-- otherwise, it just looks like the progress bar froze  
  19.         // which doesn't look great  
  20.         mAppsCustomizeTabHost.post(setAllAppsRunnable);  
  21.     } else {  
  22.         // If we did not initialize the spinner in onCreate, then we can directly set the  
  23.         // list of applications without waiting for any progress bars views to be hidden.  
  24.         setAllAppsRunnable.run();  
  25.     }  
  26. }  


三、显示应用图标

我们再来看下Launcher的onClick函数,当调用showWorkspace可以显示所有应用的图标。

  1. public void onClick(View v) {  
  2.     // Make sure that rogue clicks don't get through while allapps is launching, or after the  
  3.     // view has detached (it's possible for this to happen if the view is removed mid touch).  
  4.     if (v.getWindowToken() == null) {  
  5.         return;  
  6.     }  
  7.   
  8.     if (!mWorkspace.isFinishedSwitchingState()) {  
  9.         return;  
  10.     }  
  11.   
  12.     Object tag = v.getTag();  
  13.     if (tag instanceof ShortcutInfo) {  
  14.         // Open shortcut  
  15.         final Intent intent = ((ShortcutInfo) tag).intent;  
  16.         int[] pos = new int[2];  
  17.         v.getLocationOnScreen(pos);  
  18.         intent.setSourceBounds(new Rect(pos[0], pos[1],  
  19.                 pos[0] + v.getWidth(), pos[1] + v.getHeight()));  
  20.   
  21.         boolean success = startActivitySafely(v, intent, tag);  
  22.   
  23.         if (success && v instanceof BubbleTextView) {  
  24.             mWaitingForResume = (BubbleTextView) v;  
  25.             mWaitingForResume.setStayPressed(true);  
  26.         }  
  27.     } else if (tag instanceof FolderInfo) {  
  28.         if (v instanceof FolderIcon) {  
  29.             FolderIcon fi = (FolderIcon) v;  
  30.             handleFolderClick(fi);  
  31.         }  
  32.     } else if (v == mAllAppsButton) {  
  33.         if (isAllAppsVisible()) {  
  34.             showWorkspace(true);  
  35.         } else {  
  36.             onClickAllAppsButton(v);  
  37.         }  
  38.     }  
  39. }  

在showWorkspace中会显示所有的图标

  1. void showWorkspace(boolean animated, Runnable onCompleteRunnable) {  
  2.     if (mState != State.WORKSPACE) {  
  3.         boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);  
  4.         mWorkspace.setVisibility(View.VISIBLE);  
  5.         hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);  
  6.   
  7.         // Show the search bar (only animate if we were showing the drop target bar in spring  
  8.         // loaded mode)  
  9.         if (mSearchDropTargetBar != null) {  
  10.             mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);  
  11.         }  
  12.   
  13.         // We only need to animate in the dock divider if we're going from spring loaded mode  
  14.         showDockDivider(animated && wasInSpringLoadedMode);  
  15.   
  16.         // Set focus to the AppsCustomize button  
  17.         if (mAllAppsButton != null) {  
  18.             mAllAppsButton.requestFocus();  
  19.         }  
  20.     }  
  21.   
  22.     mWorkspace.flashScrollingIndicator(animated);  
  23.   
  24.     // Change the state *after* we've called all the transition code  
  25.     mState = State.WORKSPACE;  
  26.   
  27.     // Resume the auto-advance of widgets  
  28.     mUserPresent = true;  
  29.     updateRunning();  
  30.   
  31.     // Send an accessibility event to announce the context change  
  32.     getWindow().getDecorView()  
  33.             .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);  
  34. }  

而点击应用图标,最终会调用Launcher.java的startActivitySafely来启动应用。这里调用的startActivity就是Activity的startActivity函数。

  1. boolean startActivitySafely(View v, Intent intent, Object tag) {  
  2.     boolean success = false;  
  3.     try {  
  4.         success = startActivity(v, intent, tag);  
  5.     } catch (ActivityNotFoundException e) {  
  6.         Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();  
  7.         Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);  
  8.     }  
  9.     return success;  

猜你喜欢

转载自blog.csdn.net/q1183345443/article/details/53127384