1、Workspace載入呼叫過程,如圖
程式碼入口: /packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java, public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
TraceHelper.beginSection(TAG);
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
loadWorkspace();
...
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
TraceHelper.partitionSection(TAG, "Cancelled");
}
TraceHelper.endSection(TAG);
}
}
···
複製程式碼
接下來是總體呼叫過程:
private void loadWorkspace() {
...
LauncherSettings.Settings.call(contentResolver,
LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
...
}
// If there are any empty screens remove them, and update.
if (unusedScreens.size() != 0) {
mBgDataModel.workspaceScreens.removeAll(unusedScreens);
LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
}
}
複製程式碼
/packages/apps/Launcher3/src/com/android/launcher3/LauncherSettings.java
public static final class Settings {
...
public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
...
public static Bundle call(ContentResolver cr, String method) {
return cr.call(CONTENT_URI, method, null, null);
}
...
}
複製程式碼
/packages/apps/Launcher3/src/com/android/launcher3/LauncherProvider.java
@Override
public Bundle call(String method, final String arg, final Bundle extras) {
Log.e(TAG,"method-- "+method);
if (Binder.getCallingUid() != Process.myUid()) {
return null;
}
createDbIfNotExists();
switch (method) {
...
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
loadDefaultFavoritesIfNecessary();
return null;
}
...
}
}
複製程式碼
2、具體載入流程,是這個方法 loadDefaultFavoritesIfNecessary(),
/** * Loads the default workspace based on the following priority scheme: * 1) From the app restrictions * 2) From a package provided by play store * 3) From a partner configuration APK, already in the system image * 4) The default configuration for the particular device */
synchronized private void loadDefaultFavoritesIfNecessary() {
SharedPreferences sp = Utilities.getPrefs(getContext());
boolean aBoolean = sp.getBoolean(EMPTY_DATABASE_CREATED, false);
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
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) {
// Unable to load external layout. Cleanup and load the internal layout.
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
getDefaultLayoutParser(widgetHost));
}
clearFlagEmptyDbCreated();
}
}
複製程式碼
1) From the app restrictions
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) {
Log.e(TAG, "Target package for restricted profile not found", e);
return null;
}
}
return null;
}
複製程式碼
2) From a package provided by play store
static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback) {
Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
if (customizationApkInfo == null) {
return null;
}
return get(context, customizationApkInfo.first, customizationApkInfo.second,
appWidgetHost, callback);
}
/** Marker action used to discover a package which defines launcher customization */
static final String ACTION_LAUNCHER_CUSTOMIZATION =
"android.autoinstalls.config.action.PLAY_AUTO_INSTALL";
複製程式碼
3) From a partner configuration APK, already in the system image
...
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);
}
}
}
public static synchronized Partner get(PackageManager pm) {
if (!sSearched) {
Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
if (apkInfo != null) {
sPartner = new Partner(apkInfo.first, apkInfo.second);
}
sSearched = true;
}
return sPartner;
}
...
/** Marker action used to discover partner */
private static final String
ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
複製程式碼
4) The default configuration for the particular device
...
if (loader == null) {
loader = getDefaultLayoutParser(widgetHost);
}
...
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost){
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
int defaultLayout = idp.defaultLayoutId;
UserManagerCompat um = UserManagerCompat.getInstance(getContext());
if (um.isDemoUser() && idp.demoModeLayoutId != 0) {
defaultLayout = idp.demoModeLayoutId;
}
return new DefaultLayoutParser(getContext(), widgetHost,
mOpenHelper, getContext().getResources(), defaultLayout);
}
public class DefaultLayoutParser extends AutoInstallsLayout {
private static final String TAG = "DefaultLayoutParser";
...
public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources sourceRes, int layoutId) {
super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
}
...
}
複製程式碼
第一次載入,執行最後一個 loader = getDefaultLayoutParser(widgetHost)。 接下來,便是載入佈局檔案,解析資料,該檔案在/packages/apps/Launcher3/res/xml目錄下。具體是解析defaultLayout,對應default_workspace_3x3,default_workspace_4x4,default_workspace_5x6等的某一個檔案,這個檔案是從在InvariantDeviceProfile中獲取的。
3、載入,解析程式碼
packages/apps/Launcher3/src/com/android/launcher3/LauncherProvider.java:
...
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader)
...
@Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
ArrayList<Long> screenIds = new ArrayList<Long>();
// TODO: Use multiple loaders with fall-back and transaction.
int count = loader.loadLayout(db, screenIds);
Log.e(TAG, "loadFavorites count : "+count);
// Add the screens specified by the items above
Collections.sort(screenIds);
int rank = 0;
ContentValues values = new ContentValues();
for (Long id : screenIds) {
values.clear();
values.put(LauncherSettings.WorkspaceScreens._ID, id);
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
throw new RuntimeException("Failed initialize screen table"
+ "from default layout");
}
Log.e(TAG,"loadFavorites id: "+id+",Screenrank: "+rank);
rank++;
}
// Ensure that the max ids are initialized
mMaxItemId = initializeMaxItemId(db);
mMaxScreenId = initializeMaxScreenId(db);
Log.e(TAG,"loadFavorites favorite mMaxItemId: "+mMaxItemId+",workSpaceScreen mMaxScreenId: "+mMaxScreenId);
return count;
}
}
複製程式碼
/packages/apps/Launcher3/src/com/android/launcher3/AutoInstallsLayout.java
public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
mDb = db;
try {
return parseLayout(mLayoutId, screenIds);
} catch (Exception e) {
Log.e(TAG, "Error parsing layout: " + e);
return -1;
}
}
protected int parseLayout(int layoutId, ArrayList<Long> screenIds)
throws XmlPullParserException, IOException {
XmlResourceParser parser = mSourceRes.getXml(layoutId);
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
int type;
ArrayMap<String, TagParser> tagParserMap = getLayoutElementsMap();
int count = 0;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
Log.e(TAG,"parseLayout layoutId-- "+layoutId+",screenIds-- "+screenIds+",count-- "+count);
count += parseAndAddNode(parser, tagParserMap, screenIds);
}
return count;
}
protected int parseAndAddNode(
XmlResourceParser parser,
ArrayMap<String, TagParser> tagParserMap,
ArrayList<Long> screenIds)
throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (resId != 0) {
// recursively load some more favorites, why not?
return parseLayout(resId, screenIds);
} else {
return 0;
}
}
mValues.clear();
parseContainerAndScreen(parser, mTemp);
final long container = mTemp[0];
final long screenId = mTemp[1];
mValues.put(Favorites.CONTAINER, container);
mValues.put(Favorites.SCREEN, screenId);
mValues.put(Favorites.CELLX,
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
mValues.put(Favorites.CELLY,
convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
return 0;
}
long newElementId = tagParser.parseAndAdd(parser);
if (newElementId >= 0) {
// Keep track of the set of screens which need to be added to the db.
if (!screenIds.contains(screenId) &&
container == Favorites.CONTAINER_DESKTOP) {
screenIds.add(screenId);
}
return 1;
}
return 0;
}
複製程式碼
如圖:
總結:
1、第一次啟動Launcher,載入器是由getDefaultLayoutParser生成。如果看執行效果,可以刪除launcher.db ,重啟ActivityManager。
rm /data/data/com.android.launcher3/databases/launcher.db
adb shell am restart
複製程式碼
2、非初次啟動,EMPTY_DATABASE_CREATED=false, 不會初始化loader。
3、定製化需求,可以考慮新增對應的default_workspace.xml檔案。