1. 程式人生 > >【原始碼剖析】Launcher 8.0 原始碼 (1) --- Launcher 啟動流程 綜述

【原始碼剖析】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使用者操作處理流程,不在啟動流程裡面。

我在後面會詳細分析每一步的操作。