上篇我们讲解了系统是如何开启Launcher的,本篇开始,我们就要正式进入源码分析,这一篇里主要讲解Launcher的启动过程UI的创建。
我们已经知道Launcher是继承Activity,并且从配置文件中可以看到Launcher.java是作为主进口的,那么我们就从Launcher的onCreate开始分析。
不管Launcher如何升级版本,整体的结构流程始终没有变,从LauncherAppState开始获取手机配置,再到LauncherModel对数据进行绑定。再到数据回调给Launcher。
这里把Launcher的oncreate方法分为七步,如果是自己开发还会加上严苛模式。
onCreate的大体步骤
1.创建LauncherAppState对象,重点是根据手机硬件参数生成桌面参数(在系列第二篇中讲到的default_workspace.xml就是根据获取的硬件参数来进行适配选择的)。
2.分屏模式下的处理。
3.创建统一Launcher辅助对象。
4.生成桌面分布局。
5.计算桌面各布局细节参数。
6.LauncherModel的布局操作。
7.横屏和CallBack。
以上7步就得到了桌面的UI,如果再配合用户操作机制和后台触发机制就构成了完整的桌面。
接下来我们一一进行分析
一 开启严苛模式
如果我们在做开发Launcher的话,在oncreate中会开启严苛模式:友情链接
严苛模式主要检测两大问题,一个是线程策略ThreadPolicy,一个是VM虚拟机策略VMPolicy。
[ThreadPolicy线程策略检测]
- detectCustomSlowCalls()自定义的耗时调用。
- detectDiskReads()磁盘读取操作。
- detectDisWrites()磁盘的写入操作。
- detectNetWork()网络操作。
[VMPolicy虚拟机策略检测]
- detectActivityLeaks()Activity泄露
- detectLeakedClosableObjects()未关闭的Closable对象泄露
- detectLeakedSqllitePbjects() 泄露的Sqllite对象
- setClassInstanceLimit()检测实例数量
还有一些常用的方法:
PenaltyDialog():弹出违规提示对话框。
PenaltyLog():在Logcat中打印违规异常信息。等
这些就是策略检测的内容,StrictMode建议放在Application的oncreate中,且只在开发版本下使用,线上不适用。
二 创建LauncherAppState对象
不同的手机显示的Launcher布局是一样的,但是其中真正显示的图标,每个画面的像素点大小是不同的,Launcher需要根据手机的尺寸密度等硬件参数,计算出更多的信息,这一步就是将和手机硬件挂钩的参数都获取出来。
LauncherAppState app = LauncherAppState.getInstance(this);
// Load configuration-specific DeviceProfile
mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
这里有一个DeviceProfile,他做了什么?
一方面它定义了Launcher的很多属性,图标大小,尺寸等。
另一方面在可用间距发生改变时会调用UpdateIconSize方法,重新计算更新图标大小:行列数是根据配置的行列数,图标大小,表格间距等计算出来的,如果想要改变行列数,可以适当把图标缩小放大,间距增大或减小。
三 分屏模式下的处理
if (isInMultiWindowModeCompat()) {
Display display = getWindowManager().getDefaultDisplay();
Point mwSize = new Point();
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
分屏模式也叫做多屏模式,在多屏模式的时候,Launcher的布局有很多的变化。
四 创建统一Launcher辅助对象
mOrientation = getResources().getConfiguration().orientation;
mSharedPrefs = Utilities.getPrefs(this); //获取sharedPreferences
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);//获取LauncherModel实例
mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();//获取IconCache实例,此类主要保存图标信息,
//是在CacheLocked方法中保存图标信息,可在此方法修改图标信息。
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
mDragController = new DragController(this);//获取DragController实例,跟图标拖拽有关系
mAllAppsController = new AllAppsTransitionController(this);
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);//获取AppWidgetManager实例,用来管理widget
mAppWidgetHost = new LauncherAppWidgetHost(this);
if (Utilities.ATLEAST_MARSHMALLOW) {
mAppWidgetHost.addProviderChangeListener(this);
}
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
mPaused = false;
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
Launcher启动时启动需要用到的对象,为后面进行布局的显示做铺垫(这里只是概括讲解,后面会一一介绍)
五 生成桌面分布局
将桌面的各个部分都创建对象,绑定一些事件监听器等,进一步基本将桌面的各个UI子模块都定义完成
setupViews();
六 计算桌面各布局细节参数
UI模块的细节规划,各个模块的大小,真正的尺寸等等
mDeviceProfile.layout(this, false /* notifyListeners */);//设置控件的位置
loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.addAccessibilityStateChangeListener(this);
lockAllApps();
restoreState(savedInstanceState);
if (LauncherAppState.PROFILE_STARTUP) {
Trace.endSection();
}
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
int currentScreen = PagedView.INVALID_RESTORE_PAGE;
if (savedInstanceState != null) {
currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
}
七 LauncherModel的布局操作
不同的手机之间有区别,前几步完成不同手机的区别,保证上至平板,下至翻盖机,不同的分辨率下都能够很好的显示,手机桌面的变化重点是桌面图标布局不一样,手机中安装的软件不一样,这一步就是生成这两种布局。
if (!mModel.startLoader(currentScreen)) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
mDragLayer.setAlpha(0);
} else {
// Pages bound synchronously.
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);//设置callback接口回调
}
}
以上就是Launcher的启动流程,也是桌面的生成流程,具体的每一步会在后面进行详细讲解。