在之前我们分析了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();//获取intent ActivityInfo aInfo = resolveActivityInfo(intent,STOCK_PM_FLAGS,userID);//获取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) {//进程没有启动调用 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 */,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
<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> ......
在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 foreground mModel.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 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); } 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的。
@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 */,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 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 the user sees the progress bar // disappear-- otherwise,it just looks like the progress bar froze // which doesn't look great mAppsCustomizeTabHost.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 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,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 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); }
而点击应用图标,最终会调用Launcher.java的startActivitySafely来启动应用。这里调用的startActivity就是Activity的startActivity函数。
boolean startActivitySafely(VIEw v,Intent intent,Object tag) { boolean success = false; try { success = startActivity(v,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; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android6.0 Launcher2应用解析全部内容,希望文章能够帮你解决Android6.0 Launcher2应用解析所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)