系統(tǒng)怎么啟動Launcher的
Activity會調(diào)用startHomeActivityLocked方法,此方法會創(chuàng)建一個Intent疼约,mTopAction和mTopData傳給Intent,其中mTopAction為Intent.ACTION_MAIN缴渊,Intent的category為android.intent.category.Home矢洲。而Launcher的AndroidMainfest.xml文件里面給Launcher定義的category也是Home绅喉,根據(jù)匹配原則模狭,這樣就會啟動這個Launcher颈抚。
Launcher的intent-filter配置:
<activity
android:name="com.android.launcher3.Launcher"
...
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SHOW_WORK_APPS" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
Launcher類的onCreate初始化流程解析
protected void onCreate(Bundle savedInstanceState) {
//創(chuàng)建啟動性能日志記錄器(mStartupLatencyLogger)线梗,用于記錄Launcher啟動的性能數(shù)據(jù)嘱支。根據(jù)啟動類型(冷啟動、設(shè)備重啟丽蝎、熱啟動)進行初始化反砌。
mStartupLatencyLogger = createStartupLatencyLogger(
sIsNewProcess
? LockedUserState.get(this).isUserUnlockedAtLauncherStartup()
? COLD
: COLD_DEVICE_REBOOTING
: WARM);
sIsNewProcess = false;
mStartupLatencyLogger
.logStart(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
.logStart(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
// Only use a hard-coded cookie since we only want to trace this once.
if (Utilities.ATLEAST_S) {
Trace.beginAsyncSection(
DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
DISPLAY_ALL_APPS_TRACE_COOKIE);
}
TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT);
//如果開啟了DEBUG_STRICT_MODE雾鬼,則設(shè)置StrictMode策略萌朱,用于檢測一些運行時錯誤宴树,如磁盤讀寫、網(wǎng)絡(luò)等問題晶疼。
if (DEBUG_STRICT_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
//獲取LauncherAppState實例酒贬,該實例負(fù)責(zé)管理Launcher的狀態(tài)和數(shù)據(jù)。
LauncherAppState app = LauncherAppState.getInstance(this);
mModel = app.getModel();
//初始化設(shè)備配置翠霍,包括屏幕的旋轉(zhuǎn)信息和設(shè)備的規(guī)格信息锭吨。
mRotationHelper = new RotationHelper(this);
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
initDeviceProfile(idp);
idp.addOnChangeListener(this);
mSharedPrefs = LauncherPrefs.getPrefs(this);
mIconCache = app.getIconCache();
mAccessibilityDelegate = createAccessibilityDelegate();
//初始化Launcher的視圖(如桌面、小部件寒匙、搜索欄等)和控制器(如拖動控制器零如、應(yīng)用程序列表控制器等)。
initDragController();
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
// TODO: move the SearchConfig to SearchState when new LauncherState is created.
mBaseSearchConfig = new BaseSearchConfig();
setupViews();
//初始化小部件管理器和小部件持有者锄弱,用于管理和顯示桌面上的小部件考蕾。
mAppWidgetManager = new WidgetManagerHelper(this);
mAppWidgetHolder = createAppWidgetHolder();
mAppWidgetHolder.startListening();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
// We dont need to do anything.
savedInstanceState.remove(RUNTIME_STATE);
}
}
restoreState(savedInstanceState);
mStateManager.reapplyState();
if (savedInstanceState != null) {
int[] pageIds = savedInstanceState.getIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS);
if (pageIds != null) {
mPagesToBindSynchronously = IntSet.wrap(pageIds);
}
}
//調(diào)用LauncherModel的addCallbacksAndLoad方法,注冊回調(diào)并加載桌面數(shù)據(jù)会宪。
mStartupLatencyLogger.logWorkspaceLoadStartTime();
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, pause drawing until initial bind complete,
// so that the system could continue to show the device loading prompt
mOnInitialBindListener = Boolean.FALSE::booleanValue;
}
}
// 設(shè)置默認(rèn)的鍵盤模式
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
//設(shè)置當(dāng)前Activity的內(nèi)容視圖為Launcher的根視圖肖卧。
setContentView(getRootView());
//初始化Compose相關(guān)配置。
ComposeInitializer.initCompose(this);
if (mOnInitialBindListener != null) {
getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
}
getRootView().dispatchInsets();
// 注冊監(jiān)聽器以監(jiān)聽屏幕的開啟和關(guān)閉狀態(tài)掸鹅。
ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
mOverlayManager = getDefaultOverlay();
//注冊插件監(jiān)聽器塞帐,用于監(jiān)聽Launcher Overlay插件的狀態(tài)拦赠。
PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
LauncherOverlayPlugin.class, false /* allowedMultiple */);
//初始化屏幕旋轉(zhuǎn)輔助類,用于管理屏幕旋轉(zhuǎn)相關(guān)邏輯葵姥。
mRotationHelper.initialize();
TraceHelper.INSTANCE.endSection();
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
//結(jié)束啟動性能日志記錄器荷鼠,記錄Launcher啟動的結(jié)束時間。
mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
}
部分代碼解析:
LauncherAppState.getInstance
LauncherAppState app = LauncherAppState.getInstance(this);
// Load configuration-specific DeviceProfile
mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
創(chuàng)建LauncherAppState對象榔幸,重點是根據(jù)手機硬件參數(shù)生成桌面參數(shù)(在系列第二篇中講到的default_workspace.xml就是根據(jù)獲取的硬件參數(shù)來進行適配選擇的)颊咬。
不同的手機顯示的Launcher布局是一樣的,但是其中真正顯示的圖標(biāo)牡辽,每個畫面的像素點大小是不同的喳篇,Launcher需要根據(jù)手機的尺寸密度等硬件參數(shù),計算出更多的信息态辛,這一步就是將和手機硬件掛鉤的參數(shù)都獲取出來麸澜。
一方面它定義了Launcher的很多屬性,圖標(biāo)大小奏黑,尺寸等炊邦。
另一方面在可用間距發(fā)生改變時會調(diào)用UpdateIconSize方法,重新計算更新圖標(biāo)大惺焓贰:行列數(shù)是根據(jù)配置的行列數(shù)馁害,圖標(biāo)大小,表格間距等計算出來的蹂匹,如果想要改變行列數(shù)碘菜,可以適當(dāng)把圖標(biāo)縮小放大,間距增大或減小限寞。
生成桌面分布局
setupViews();
將桌面的各個部分都創(chuàng)建對象忍啸,綁定一些事件監(jiān)聽器等,進一步基本將桌面的各個UI子模塊都定義完成履植。
Launcher的onCreate方法之后
Activity.onCreate在接近結(jié)尾的地方調(diào)用了mModel的startLoader方法计雌,他把LoaderTask對象放到了工作線程中。
sWorkThread會執(zhí)行LoaderTask的run方法玫霎,run方法里面最重要的就屬于loadAndBindWorkspace方法了凿滤。在上面的圖中,我已經(jīng)畫出了他的功能庶近,先是在數(shù)據(jù)庫讀取數(shù)據(jù)翁脆,看看有什么需要增加,有什么需要刪除拦盹;再在界面上顯示鹃祖。顯示完了之后桌面其實也就啟動完了。
loadWorkspace:
loadWorkspace有將近400行,挺多的恬口,其實做的事情就是遍歷數(shù)據(jù)庫里的每條記錄校读,判斷他的類型,生成對應(yīng)的ItemInfo對象(ShortcutInfo祖能,F(xiàn)olderInfo歉秫,LauncherAppWidgetInfo)bindWorkspace:
在bindWorkspace里面,用了一個很重要的Callback接口养铸,Launcher.java實現(xiàn)了這些接口雁芙,用于更新UI。bindWorkspace新建了幾個對象都是current,other形式的钞螟,這個current代表的是當(dāng)前屏的ItemInfo兔甘,other代表的其他屏的ItemInfo,為了加載時候不讓用戶感覺很慢鳞滨,就先把當(dāng)前屏的顯示出來洞焙,再顯示其他的,這個顯示的工作都交給了bindWorkspaceItems方法拯啦。bindWorkspaceItems會分別加載圖標(biāo)澡匪,小工具,和文件夾褒链。
參考:
http://www.reibang.com/p/5d4e5b5c6804
https://fookwood.com/launcher-start-process-2