【源码解析】Launcher 8.0源码(4)---Launcher的源码启动过程综述

上篇我们讲解了系统是如何开启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的启动流程,也是桌面的生成流程,具体的每一步会在后面进行详细讲解。

你可能感兴趣的:(【源码解析】Launcher 8.0源码(4)---Launcher的源码启动过程综述)