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
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文件。