现在网上关于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用户操作处理流程,不在启动流程里面。
我在后面会详细分析每一步的操作。