Android6.0 Launcher2應(yīng)用解析
在之前我們分析了Android6.0系統(tǒng)在啟動(dòng)時(shí)安裝應(yīng)用程序的過(guò)程,這些應(yīng)用程序安裝好之后,Launcher應(yīng)用就負(fù)責(zé)把它們?cè)谧烂嫔险故境鰜?lái)。
一、AMS啟動(dòng)Launcher
Launcher應(yīng)用是在AMS的systemReady方法中直接調(diào)用startHomeActivityLocked啟動(dòng)的,下面是systemReady啟動(dòng)Launcher的代碼。
startHomeActivityLocked(mCurrentUserId, "systemReady");我們來(lái)看下這個(gè)函數(shù),先調(diào)用了getHomeIntent方法來(lái)獲取Intent,然后也是調(diào)用resolveActivityInfo函數(shù)從PKMS獲取ActivityInfo,接著當(dāng)進(jìn)程沒(méi)有啟動(dòng)的話,調(diào)用ActivityStackSupervisor的startHomeActivity函數(shù)
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) {//進(jìn)程沒(méi)有啟動(dòng)調(diào)用 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; }
我們先來(lái)看看getHomeIntent這個(gè)函數(shù)。
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; }
然后我們來(lái)看下ActivityStackSupervisor的startHomeActivity函數(shù),它也是調(diào)用了startActivityLocked來(lái)啟動(dòng)Activity的,在之前的博客分析過(guò)這個(gè)函數(shù)這里我們就不介紹了。
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啟動(dòng)
接著我們來(lái)看下Launcher的AndroidManifest.xml,我們看下其主Activity有一個(gè)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函數(shù)中調(diào)用了mModel.startLoader函數(shù)
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函數(shù)會(huì)post一個(gè)Runnable消息,我們來(lái)看下它的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方法中會(huì)調(diào)用loadAndBindAllApps函數(shù),在loadAndBindAllApps函數(shù)中又會(huì)調(diào)用loadAllAppsByBatch函數(shù)
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(); }
我們先來(lái)看下loadAndBindAllApps函數(shù),這個(gè)函數(shù)先進(jìn)入while循環(huán),然后調(diào)用了LauncherApps的getActivityList函數(shù),后面又會(huì)調(diào)用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"); } } }); ......
我們先來(lái)看LauncherApps的getActivityList函數(shù),它先用mService成員變量調(diào)用getLauncherActivities函數(shù)獲取到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函數(shù),肯定也是通過(guò)PKMS來(lái)獲取相關(guān)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); } }
最后回調(diào)Launcher.java的bindAllApplications函數(shù),最后在這個(gè)函數(shù)中可以在桌面上展示系統(tǒng)中所有的應(yīng)用程序了。
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(); } }
三、顯示應(yīng)用圖標(biāo)
我們?cè)賮?lái)看下Launcher的onClick函數(shù),當(dāng)調(diào)用showWorkspace可以顯示所有應(yīng)用的圖標(biāo)。
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, 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中會(huì)顯示所有的圖標(biāo)
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); }
而點(diǎn)擊應(yīng)用圖標(biāo),最終會(huì)調(diào)用Launcher.java的startActivitySafely來(lái)啟動(dòng)應(yīng)用。這里調(diào)用的startActivity就是Activity的startActivity函數(shù)。
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; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 適配android7.0獲取文件的Uri的方法
- Android7.0 工具類:DiffUtil詳解
- Android7.0 MessageQueue詳解
- Android7.0上某些PopuWindow出現(xiàn)顯示位置不正確問(wèn)題的解決方法
- Android開(kāi)發(fā)實(shí)現(xiàn)Launcher3應(yīng)用列表修改透明背景的方法
- Android開(kāi)發(fā)中Launcher3常見(jiàn)默認(rèn)配置修改方法總結(jié)
- Android launcher中模擬按home鍵的實(shí)現(xiàn)
- Android的Launcher啟動(dòng)器中添加快捷方式及小部件實(shí)例
- Android實(shí)現(xiàn)向Launcher添加快捷方式的方法
- Android7.0開(kāi)發(fā)實(shí)現(xiàn)Launcher3去掉應(yīng)用抽屜的方法詳解
相關(guān)文章
android動(dòng)態(tài)布局之動(dòng)態(tài)加入TextView和ListView的方法
這篇文章主要介紹了android動(dòng)態(tài)布局之動(dòng)態(tài)加入TextView和ListView的方法,涉及Android動(dòng)態(tài)布局的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-05-05Android實(shí)現(xiàn)跟隨手指拖動(dòng)并自動(dòng)貼邊的View樣式(實(shí)例demo)
本文通過(guò)實(shí)例代碼給大家介紹了android實(shí)現(xiàn)跟隨手指拖動(dòng)并自動(dòng)貼邊的View樣式,效果非常棒,具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01Android studio 3.0 查看手機(jī)文件系統(tǒng)的方法(超簡(jiǎn)單)
本文給大家分享Android studio更新到3.0版本之后,查看手機(jī)文件系統(tǒng)的方法,需要的朋友參考下吧2017-11-11Android實(shí)現(xiàn)指針刻度轉(zhuǎn)盤
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)指針刻度轉(zhuǎn)盤,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08Android RecyclerView顯示Item布局不一致解決辦法
這篇文章主要介紹了Android RecyclerView顯示Item布局不一致解決辦法的相關(guān)資料,需要的朋友可以參考下2017-07-07Android編程設(shè)計(jì)模式之抽象工廠模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之抽象工廠模式,結(jié)合實(shí)例形式詳細(xì)分析了Android抽象工廠模式的概念、原理、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-12-12完美解決Android客戶端RSA解密部分亂碼的問(wèn)題
下面小編就為大家分享一篇完美解決Android客戶端RSA解密部分亂碼的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Android中在GridView網(wǎng)格視圖上實(shí)現(xiàn)item拖拽交換的方法
這篇文章主要介紹了Android中在GridView上實(shí)現(xiàn)item拖拽交換效果的方法,比起ListView的列表?xiàng)l目交換稍顯復(fù)雜,文中先介紹了關(guān)于創(chuàng)建GridView的一些基礎(chǔ)知識(shí),需要的朋友可以參考下2016-04-04Android列表組件ListView使用詳解之動(dòng)態(tài)加載或修改列表數(shù)據(jù)
今天小編就為大家分享一篇關(guān)于Android列表組件ListView使用詳解之動(dòng)態(tài)加載或修改列表數(shù)據(jù),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03