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 工具類(lèi):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調(diào)用系統(tǒng)分享給微信,出現(xiàn)分享失敗,分享多文件必須為圖片格式的問(wèn)題
這篇文章主要介紹了解決Android調(diào)用系統(tǒng)分享給微信,出現(xiàn)分享失敗,分享多文件必須為圖片格式的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Android BroadcastReceiver廣播簡(jiǎn)單使用
這篇文章主要為大家詳細(xì)介紹了Android BroadcastReceiver廣播簡(jiǎn)單的使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
Flutter移動(dòng)端進(jìn)行多渠道打包發(fā)布的全過(guò)程
在使用flutter開(kāi)發(fā)的過(guò)程中,需要根據(jù)不同的環(huán)境,不同的包名來(lái)打包,下面這篇文章主要給大家介紹了關(guān)于Flutter移動(dòng)端進(jìn)行多渠道打包發(fā)布的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
adb wireless進(jìn)行Android手機(jī)調(diào)試詳解
這篇文章給大家講解了在Android手機(jī)上使用adb wireless進(jìn)行調(diào)試的步驟以及問(wèn)題解決辦法,有需要的跟著學(xué)習(xí)下吧。2017-12-12
android view轉(zhuǎn)Bitmap生成截圖的方法
這篇文章主要介紹了android view轉(zhuǎn)Bitmap生成截圖的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Android?Room數(shù)據(jù)庫(kù)加密詳解
這篇文章主要為大家詳細(xì)介紹了Android?Room數(shù)據(jù)庫(kù)加密,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Android TCP 文件客戶(hù)端與服務(wù)器DEMO介紹
首先是服務(wù)器,服務(wù)器是在PC機(jī)上,JAVA運(yùn)行環(huán)境,主要參考網(wǎng)上的代碼,自己做了支持多線程處理,代碼如下所示。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11
Android Studio提示inotify大小不足的解決辦法
大家在使用Android Studio導(dǎo)入AOSP源碼的時(shí)候,可能會(huì)遇到inotify大小不足的問(wèn)題,這篇文章就給大家介紹了怎么解決這個(gè)問(wèn)題的方法,有需要的朋友們可以參考借鑒。2016-09-09

