【源码剖析】Launcher 8.0 源码 (14) --- Launcher 启动流程 第六步之LoadWorkspace 第1小步(2)寻找布局xml的范围

Launcher8.0启动流程的第六步中loadworkspace的第1小步是获取数据库,首先完成第一个操作,判断有没有现成的数据库,如果没有现成数据库,则创建一个新的数据库,并且从手机中读取相应的布局。

也就是第1小步的第二个操作,寻找布局文件的范围。

loadDefaultFavoritesIfNecessary其源码如下:

synchronized private void loadDefaultFavoritesIfNecessary() {
    SharedPreferences sp = Utilities.getPrefs(getContext());
    if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
        Log.d(TAG, "loading default workspace");
        AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
        AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);

        if (loader == null) {
            loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
        }

        if (loader == null) {
            final Partner partner = Partner.get(getContext().getPackageManager());
            if (partner != null && partner.hasDefaultLayout()) {
                final Resources partnerRes = partner.getResources();
                int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                        "xml", partner.getPackageName());
                if (workspaceResId != 0) {
                    loader = new DefaultLayoutParser(getContext(), widgetHost,
                            mOpenHelper, partnerRes, workspaceResId);
                }
            }
        }
        final boolean usingExternallyProvidedLayout = loader != null;


        if (loader == null) {
            loader = getDefaultLayoutParser(widgetHost);
        }

        mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
        if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
                && usingExternallyProvidedLayout) {
            mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
                    getDefaultLayoutParser(widgetHost));
        }
        clearFlagEmptyDbCreated();
    }
}

loadDefaultFavoritesIfNecessary首先要获取正确的loader对象:根据代码获取各个场景下的布局,先是判断一些特殊情况,如果特殊情况没有xml布局,则读取本地res/xml的布局。

synchronized private void loadDefaultFavoritesIfNecessary() {
    SharedPreferences sp = Utilities.getPrefs(getContext());
    if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
        Log.d(TAG, "loading default workspace");
        AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
        AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);

Loader第一个是判断有没有Restriction。

 targetResources通过关键字RESTRICTION_PACKAGE_NAME 寻找,符合package名字为"workspace.configuration.package.name"的应用,如果有这个应用,则从其中获取布局。

源码如下:

private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
    Context ctx = getContext();
    UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
    Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName());
    if (bundle == null) {
        return null;
    }
    String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME);
    if (packageName != null) {
        try {
            Resources targetResources = ctx.getPackageManager()
                    .getResourcesForApplication(packageName);
            return AutoInstallsLayout.get(ctx, packageName, targetResources,
                    widgetHost, mOpenHelper);
        } catch (NameNotFoundException e) {
            return null;
        }
    }
    return null;
}

接下来是第二种loader的情况:AutoInstallsLayout。

 if (loader == null) {
            loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
        }

依然是AutoInstallsLayout,这次是从intent 关键字ACTION_LAUNCHER_CUSTOMIZATION即是"android.autoinstalls.config.action.PLAY_AUTO_INSTALL"来获取,autoinstall可以在手机中集成对应工具,这样默认布局除了手机自带的应用外,还可以提供一些自动下载的应用。 

源码如下:

static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
        LayoutParserCallback callback) {
    Pair customizationApkInfo = Utilities.findSystemApk(
            ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
    if (customizationApkInfo == null) {
        return null;
    }
    return get(context, customizationApkInfo.first, customizationApkInfo.second,
            appWidgetHost, callback);
}

当两种AutoInstallsLayout失败后,就到第三种loader是判断有没有第三方应用提供布局。这个功能是提供给提供布局的第三方应用。第三方应用提供一定的布局,然后清空Launcher的数据,之后Launcher的布局就和第三方一样了,这是一个让第三方应用改变桌面布局的。

这次的关键词是"com.android.launcher3.action.PARTNER_CUSTOMIZATION"  这个关键词出现在 Partner.get( 方法中。 如果系统中能搜索到该关键词的应用,则采用该应用的布局。和autoinstall不同,这次一定是读取一个叫做“partner_default_layout.xml”的文件

源码如下:

if (loader == null) {
    final Partner partner = Partner.get(getContext().getPackageManager());
    if (partner != null && partner.hasDefaultLayout()) {
        final Resources partnerRes = partner.getResources();
        int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                "xml", partner.getPackageName());
        if (workspaceResId != 0) {
            loader = new DefaultLayoutParser(getContext(), widgetHost,
                    mOpenHelper, partnerRes, workspaceResId);
        }
    }
}

如果以上三种都没读到信息,则进入第4种,也是最常用的一种,我们能控制的本地布局。以上三种没读到,包括没有对应应用和对应应用没有对于xml文件,都会导致返回空。

源码如下:

if (loader == null) {
    loader = getDefaultLayoutParser(widgetHost);
}

private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
    int defaultLayout = LauncherAppState.getIDP(getContext()).defaultLayoutId;
    return new DefaultLayoutParser(getContext(), widgetHost,
            mOpenHelper, getContext().getResources(), defaultLayout);
}

public static InvariantDeviceProfile getIDP(Context context) {
    return LauncherAppState.getInstance(context).getInvariantDeviceProfile();
}

是不是非常眼熟,正式LauncheronCreate第一步LauncherAppState构造器中InvariantDeviceProfile从google预置的各种布局参数中选取的与当前手机型号最接近的参数群,defaultLayoutId是在参数群中用于识别参数群的。参数群里面提供了xml文件等各项参数。最终形成布局。

loadDefaultFavoritesIfNecessary的第1小步的第二个操作获取loader也就是获取对应的xml文件。

你可能感兴趣的:(源码剖析)