【原始碼剖析】Launcher 8.0 原始碼 (1) --- Launcher 啟動流程 綜述
現在網上關於Launcher啟動流程的原始碼分析流傳最多的是google Launcher2.0的啟動流程。截止2018年5月,google Launcher已經到了8.0版本。
經對比,8.0和2.0的啟動流程大同小異,整體流程依然保留了2.0的結構特徵,以LauncherAppState開始獲取手機各項引數,從Launcher到LauncherModel再由LauncherModel到Launcher的回撥的合入桌面佈局和allapp列表。
我個人以程式碼邏輯和程式碼量兩者結合,將Launcher的oncreate程式碼分為了7個步驟:
1:建立LauncherAppState 物件,重點是根據手機硬體引數,生成桌面引數。
2:分屏模式的處理
3:統一建立物件
4:生成桌面分佈局
5:計算桌面各佈局細節引數
6:LauncherModel的佈局操作
7:橫屏和callback
以上7步就得到桌面的UI。
如果在配合使用者操作機制和後臺觸發機制就構成了完整的桌面。Launcher的啟動機制會繫結使用者操作和後臺觸發,但是不展開講解具體的內容。
通過對Launcher啟動流程的分析可以瞭解到整個Launcher的框架和架構,也能窺豹一斑的體會Launcher的完成面貌。
Launcher8.0的oncreate()方法原始碼如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (mLauncherCallbacks != null) {
mLauncherCallbacks.preOnCreate();
}
super.onCreate(savedInstanceState);
//第一步建立LauncherAppState 物件。不同的手機顯示的Launcher佈局是一樣的,但是其中真正顯示的每個圖示,每個畫面的畫素點大小是不同的。Launcher需要根據手機的尺寸密度等引數,計算出更多的資訊。第一步是將和手機硬體掛鉤的引數都獲取出來。
LauncherAppState app = LauncherAppState.getInstance(this);
mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
//第二步,分屏模式也叫做多屏模式,在多屏模式的時候,Launcher的佈局有很多的變化。
if (isInMultiWindowModeCompat()) {
Display display = getWindowManager().getDefaultDisplay();
Point mwSize = new Point();
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
//第三步統一建立物件,Launcher啟動時需要用到的物件,在這裡統一建立,為後面進行佈局的顯示進行鋪墊。
mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateTransitionAnimation=new LauncherStateTransitionAnimation(this, mAllAppsController);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
mPaused = false;
mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
//第四步生成桌面分佈局,將桌面的各個部分都建立物件,繫結一些事件監聽器等,這一步基本將桌面的各個UI子模組都定義完成。
setupViews();
//第五步,UI子模組的細節規劃,各個模組的大小,真正的尺寸等等。這一步是採用第一步獲取的方案,把第四步的模組細節進行完成。
mDeviceProfile.layout(this, false /* notifyListeners */);
mExtractedColors = new ExtractedColors();
loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
lockAllApps();
restoreState(savedInstanceState);
if (LauncherAppState.PROFILE_STARTUP) {
Trace.endSection();
}
int currentScreen = PagedView.INVALID_RESTORE_PAGE;
if (savedInstanceState != null) {
currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
}
//第六步,生成佈局。Launcher不是一張圖片,因為不同的手機之間有區別。前五步完成不同手機的區別,保證上至平板,下至翻蓋機,不同的解析度下都能夠很好的顯示。而手機桌面的變化重點是桌面圖示佈局不一樣,手機中安裝的軟體不一樣。第六步就是生成這兩種佈局。
if (!mModel.startLoader(currentScreen)) {
mDragLayer.setAlpha(0);
} else {
mWorkspace.setCurrentPage(currentScreen);
setWorkspaceLoading(true);
}
//第七步,橫屏和CallBack等善後
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
if (!mRotationEnabled) {
mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
mRotationPrefChangeHandler = new RotationPrefChangeHandler();
mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
}
if (PinItemDragListener.handleDragRequest(this, getIntent())) {
mRotationEnabled = true;
}
setOrientation();
setContentView(mLauncherView);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
以上是Launcher的啟動流程,也是生成桌面的流程。個人認為最重要的兩部分,第一部分是第一步和第四步,知道Launcher的整體UI顯示框架是怎麼來的。 第二部分是第六步,知道桌面的最大特點,自定義圖示擺放是怎麼讀取的。
最後關於自定義圖示擺放的操作屬於Launcher使用者操作處理流程,不在啟動流程裡面。
我在後面會詳細分析每一步的操作。