Launcher3 桌面加载流程分析

Launcher3 桌面加载流程分析

主入口Launcher

首先来看Launcher.java的onCreate方法,里面代码很多,只看主流程部分:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ......
    LauncherAppState app = LauncherAppState.getInstance(this);
    ......
}

这里的LauncherAppState类是用来保存一些全局的、核心的对象。主要有整个Launcher的工作台workspace、Launcher的控制器LauncherModel、Launcher的应用图标缓存机制 IconCache,以及设备的配置信息InvarianDeviceProfile等等。比如说核心对象IconCache、LauncherModel就是在这里面进行初始化的。

接着看onCreate中的代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ......
    LauncherAppState app = LauncherAppState.getInstance(this);
    
    mOldConfig = new Configuration(getResources().getConfiguration());
    mModel = app.getModel();
    mRotationHelper = new RotationHelper(this);
    InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
    ......
}

通过Configuration获取屏幕的配置,通过LauncherAppState获取到LauncherModel的实例对象以及InvariantDeviceProfile的实例对象(在这个类中会去获取设备的配置信息、硬件参数等等)。

在InvariantDeviceProfile的getPredefinedDeviceProfiles中会去device_profiles配置文件中去适配对应的配置信息:

private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName, boolean isSplitDisplay) {
    ArrayList<DisplayOption> profiles = new ArrayList<>();
    try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
        final int depth = parser.getDepth();
        int type;
        ......
}

device_profiles.xml就不贴出来了,当新的机型没有适配,就可以在这里进行修改或新增配置。

…算了,还是贴一部分出来吧,自己也方便看:

<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" >

    <grid-option
        launcher:name="3_by_3"
        launcher:numRows="3"
        launcher:numColumns="3"
        launcher:numFolderRows="2"
        launcher:numFolderColumns="3"
        launcher:numHotseatIcons="3"
        launcher:dbFile="launcher_3_by_3.db"
        launcher:defaultLayoutId="@xml/default_workspace_3x3" >

        <display-option
            launcher:name="Super Short Stubby"
            launcher:minWidthDps="255"
            launcher:minHeightDps="300"
            launcher:iconImageSize="48"
            launcher:iconTextSize="13.0"
            launcher:canBeDefault="true" />

        <display-option
            launcher:name="Shorter Stubby"
            launcher:minWidthDps="255"
            launcher:minHeightDps="400"
            launcher:iconImageSize="48"
            launcher:iconTextSize="13.0"
            launcher:canBeDefault="true" />

    grid-option>
    ........

​ 到这里Launcher的配置初始化到这里就基本加载完成了,还有一些其他的对象的初始化,包括workspace状态变化的动画加载控制LauncherStateTransitionAnimation, 应用组件的管理器AppWidgetManagerCompat, 处理组件长按事件的ViewLauncherAppWidgetHost之类的就没有去管。

LauncherModel加载应用信息

LauncherModel中有一个很重要的方法:startLoader

/**
 * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
 * @return true if the page could be bound synchronously.
 */
public boolean startLoader() {
    // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
    ItemInstallQueue.INSTANCE.get(mApp.getContext())
        .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
    synchronized (mLock) {
        // Don't bother to start the thread if we know it's not going to do anything
        final Callbacks[] callbacksList = getCallbacks();
        if (callbacksList.length > 0) {
            // Clear any pending bind-runnables from the synchronized load process.
            for (Callbacks cb : callbacksList) {
                MAIN_EXECUTOR.execute(cb::clearPendingBinds);
            }

            // If there is already one running, tell it to stop.
            stopLoader();
            LoaderResults loaderResults = new LoaderResults(mApp, mBgDataModel, mBgAllAppsList, callbacksList);
            if (mModelLoaded && !mIsLoaderTaskRunning) {
                // Divide the set of loaded items into those that we are binding synchronously,
                // and everything else that is to be bound normally (asynchronously).
                loaderResults.bindWorkspace();
                // For now, continue posting the binding of AllApps as there are other
                // issues that arise from that.
                loaderResults.bindAllApps();
                loaderResults.bindDeepShortcuts();
                loaderResults.bindWidgets();
                return true;
            } else {
                stopLoader();
                mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);

                // Always post the loader task, instead of running directly
                // (even on same thread) so that we exit any nested synchronized blocks
                MODEL_EXECUTOR.post(mLoaderTask);
            }
        }
    }
    return false;
}

可以看出该方法中创建了LoaderTask用来加载数据,该类实现了Runnable接口,我们直接看它的run方法:

public void run() {
	synchronized (this) {
            // Skip fast if we are already stopped.
        	//中文:如果我们已经停止快速跳过
            if (mStopped) {
                return;
            }
    }
    ......
    ......
    try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            loadWorkspace(allShortcuts);
            ......
            ......        
        mResults.bindWorkspace();
        	......
		// Take a break
        waitForIdle();
        logASplit(logger, "step 1 complete");
        verifyNotStopped();
        ......
        ......
        verifyNotStopped();
        mResults.bindAllApps();
        ......

}

首先根据mStopped的状态进行一个判断,然后最先执行的是loadWorkspace方法,里面是对Workspace的一些加载,包括屏幕数、应用数据、widget组建信息等等,代码很多就不贴出来了。

接下来会调用mResults.bindWorkspace进行一个绑定,然后就会调用waitForIdle方法进行一个休息,等待其它子线程执行完,代码中在多个地方都进行了等待,有兴趣的可以自己去看看,这里并没有都贴出来。

再往下就是verifyNotStopped方法,在该方法中对mStopped又进行了一次判断,如果为true就抛异常,否则什么也不做。

最后就是调用mResults.bindAllApps方法绑定所有app,mResults.bindWidgets绑定小部件等等的一些列绑定。

小结:Launcher里面数据比较多,包括所有应用的图标和应用数据,所有应用的Widget数据,桌面已添加的用户数据等,随着Android大版本演进,还有DeepShortcuts等新的数据类型。如果按照常规的加载做法,等加载数据完成后再显示到View,耗时就太长了。为了优化体验,Launcher于是采用了分批加载、分批绑定的做法。

整体加载绑定流程如下:

在这里插入图片描述

最后会调用Launcher里实现的回调方法bindAllApplications,将数据填充到View容器里:

/**
* Add the icons for all apps.  
*	为应用程序添加图标
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
public void bindAllApplications(AppInfo[] apps, int flags) {
    mAppsView.getAppsStore().setApps(apps, flags);
    PopupContainerWithArrow.dismissInvalidPopup(this);
}

就拿Workspace的加载为例:

Launcher3 桌面加载流程分析_第1张图片

另外AllApps和Widget的加载流程都差不太多。

基本上整体的流程就是这样…

你可能感兴趣的:(Android基础,java,android,开发语言)