Launcher是一个特殊的App,属于系统软件,在按home键时会启动的App,在你的Activity中加入如下intent-fliter 的category之后就会被系统当作Launcher应用。
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
一般我们在给视图绑定数据的时候会把它写在主线程onCreate中,如果需要加载时间,我们会用线程去辅助加载数据,Launcher启动时需要加载好App,shortcut,Folder等一系列的item,所以Launcher采用了Runable来加载数据,并且定义了一个规范的LauncherModel.Callback接口来定义具体的加载过程,步骤。
Launcher本身就是一个框架,从首次启动读取自定义的配置文件开始,操作的过程不断的更新db中存储的信息。下面来简单介绍启动流程。
那么具体到代码是如何启动并绑定数据的呢?
首先定义了接口:LauncherModel.Callback 看名字就知道是什么意思了。
public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void startBinding();
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
public void bindFolders(HashMap<Long, FolderInfo> folders);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList<ApplicationInfo> apps);
public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<String> packageNames, boolean permanent);
public void bindPackagesUpdated();
public boolean isAllAppsVisible();
public boolean isAllAppsButtonRank(int rank);
public void bindSearchablesChanged();
public void onPageBoundSynchronously(int page);
}
该类中还定义了一个线程:该类就负责读书default_workspace.xml文件,读取数据库等其他的耗时操作。
private class LoaderTask implements Runnable {}
private static class DatabaseHelper extends SQLiteOpenHelper {}
private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ContentValues values = new ContentValues();
PackageManager packageManager = mContext.getPackageManager();
int allAppsButtonRank = mContext.getResources().getInteger(
R.integer.hotseat_all_apps_index);
int i = 0;
try {
XmlResourceParser parser = mContext.getResources().getXml(
workspaceResourceId);
AttributeSet attrs = Xml.asAttributeSet(parser);
beginDocument(parser, TAG_FAVORITES);
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG || parser
.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
boolean added = false;
final String name = parser.getName();
TypedArray a = mContext.obtainStyledAttributes(attrs,
R.styleable.Favorite);
long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
if (a.hasValue(R.styleable.Favorite_container)) {
container = Long.valueOf(a
.getString(R.styleable.Favorite_container));
}
String screen = a.getString(R.styleable.Favorite_screen);
String x = a.getString(R.styleable.Favorite_x);
String y = a.getString(R.styleable.Favorite_y);
// If we are adding to the hotseat, the screen is used as
// the position in the
// hotseat. This screen can't be at position 0 because
// AllApps is in the
// zeroth position.
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
&& Integer.valueOf(screen) == allAppsButtonRank) {
throw new RuntimeException(
"Invalid screen position for hotseat item");
}
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER, container);
values.put(LauncherSettings.Favorites.SCREEN, screen);
values.put(LauncherSettings.Favorites.CELLX, x);
values.put(LauncherSettings.Favorites.CELLY, y);
if (TAG_FAVORITE.equals(name)) {
long id = addAppShortcut(db, values, a, packageManager,
intent);
added = id >= 0;
}else if(TAG_TINYACTION.equals(name)){
long id=addTinyAction(db, values, a, intent);//写入数据库
added = id >= 0;
} else if (TAG_SEARCH.equals(name)) {
added = addSearchWidget(db, values);
} else if (TAG_CLOCK.equals(name)) {
added = addClockWidget(db, values);
} else if (TAG_APPWIDGET.equals(name)) {
added = addAppWidget(parser, attrs, type, db, values,
a, packageManager);
} else if (TAG_SHORTCUT.equals(name)) {
long id = addUriShortcut(db, values, a);
added = id >= 0;
} else if (TAG_FOLDER.equals(name)) {
String title;
int titleResId = a.getResourceId(
R.styleable.Favorite_title, -1);
if (titleResId != -1) {
title = mContext.getResources().getString(
titleResId);
} else {
title = mContext.getResources().getString(
R.string.folder_name);
}
values.put(LauncherSettings.Favorites.TITLE, title);
long folderId = addFolder(db, values);
added = folderId >= 0;
ArrayList<Long> folderItems = new ArrayList<Long>();
int folderDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > folderDepth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String folder_item_name = parser.getName();
TypedArray ar = mContext.obtainStyledAttributes(
attrs, R.styleable.Favorite);
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER,
folderId);
if (TAG_FAVORITE.equals(folder_item_name)
&& folderId >= 0) {
long id = addAppShortcut(db, values, ar,
packageManager, intent);
if (id >= 0) {
folderItems.add(id);
}
} else if (TAG_SHORTCUT.equals(folder_item_name)
&& folderId >= 0) {
long id = addUriShortcut(db, values, ar);
if (id >= 0) {
folderItems.add(id);
}
} else {
throw new RuntimeException("Folders can "
+ "contain only shortcuts");
}
ar.recycle();
}
// We can only have folders with >= 2 items, so we need
// to remove the
// folder and clean up if less than 2 items were
// included, or some
// failed to add, and less than 2 were actually added
if (folderItems.size() < 2 && folderId >= 0) {
// We just delete the folder and any items that made
// it
deleteId(db, folderId);
if (folderItems.size() > 0) {
deleteId(db, folderItems.get(0));
}
added = false;
}
}
if (added)
i++;
a.recycle();
}
} catch (XmlPullParserException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
} catch (IOException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
} catch (RuntimeException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
}
return i;
}
写入之后就是再从表中读出来,封装成相应的数据类型,然后显示在界面上。
LauncherModel中的LoaderTask线程的loadFavorit()方法读取db中的信息:
private void loadWorkspace() {}
private void loadWorkspace() {
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
final ContentResolver contentResolver = context.getContentResolver();
final PackageManager manager = context.getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final boolean isSafeMode = manager.isSafeMode();
// Make sure the default workspace is loaded, if needed
mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
synchronized (sBgLock) {
sBgWorkspaceItems.clear();
sBgAppWidgets.clear();
sBgFolders.clear();
sBgItemsIdMap.clear();
sBgDbIconCache.clear();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final Cursor c = contentResolver.query(LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
// +1 for the hotseat (it can be larger than the workspace)
// Load workspace in reverse order to ensure that latest items
// are loaded first (and
// before any earlier duplicates)
final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];
try {
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int shortcutTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SHORTCUT_TYPE);
final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
// final int uriIndex =
// c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
// final int displayModeIndex = c.getColumnIndexOrThrow(
// LauncherSettings.Favorites.DISPLAY_MODE);
ShortcutInfo info;
String intentDescription;
LauncherAppWidgetInfo appWidgetInfo;
int container;
long id;
Intent intent;
while (!mStopped && c.moveToNext()) {
try {
int itemType = c.getInt(itemTypeIndex);
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
intentDescription = c.getString(intentIndex);
try {
intent = Intent.parseUri(intentDescription, 0);
} catch (URISyntaxException e) {
continue;
}
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getShortcutInfo(manager, intent, context, c, iconIndex, titleIndex, mLabelCache);
} else {
info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex,
iconIndex, titleIndex, shortcutTypeIndex);
// App shortcuts that used to be
// automatically added to Launcher
// didn't always have the correct intent
// flags set, so do that
// here
if (intent.getAction() != null && intent.getCategories() != null
&& intent.getAction().equals(Intent.ACTION_MAIN)
&& intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
}
if (info != null) {
info.intent = intent;
info.id = c.getLong(idIndex);
container = c.getInt(containerIndex);
info.container = container;
info.screen = c.getInt(screenIndex);
info.cellX = c.getInt(cellXIndex);
info.cellY = c.getInt(cellYIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, info)) {
break;
}
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sBgWorkspaceItems.add(info);
break;
default:
// Item is in a user folder
FolderInfo folderInfo = findOrMakeFolder(sBgFolders, container);
folderInfo.add(info);
break;
}
sBgItemsIdMap.put(info.id, info);
// now that we've loaded everthing re-save
// it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
} else {
// Failed to load the shortcut, probably
// because the
// activity manager couldn't resolve it
// (maybe the app
// was uninstalled), or the db row was
// somehow screwed up.
// Delete it.
id = c.getLong(idIndex);
Log.e(TAG, "Error loading shortcut " + id + ", removing it");
contentResolver.delete(LauncherSettings.Favorites.getContentUri(id, false), null, null);
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
folderInfo.title = c.getString(titleIndex);
folderInfo.id = id;
container = c.getInt(containerIndex);
folderInfo.container = container;
folderInfo.screen = c.getInt(screenIndex);
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo)) {
break;
}
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sBgWorkspaceItems.add(folderInfo);
break;
}
sBgItemsIdMap.put(folderInfo.id, folderInfo);
sBgFolders.put(folderInfo.id, folderInfo);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
// Read all Launcher-specific widget details
int appWidgetId = c.getInt(appWidgetIdIndex);
id = c.getLong(idIndex);
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(appWidgetId);
if (!isSafeMode
&& (provider == null || provider.provider == null || provider.provider.getPackageName() == null)) {
String log = "Deleting widget that isn't installed anymore: id=" + id + " appWidgetId="
+ appWidgetId;
Log.e(TAG, log);
Launcher.sDumpLogs.add(log);
itemsToRemove.add(id);
} else {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provider.provider);
appWidgetInfo.id = id;
appWidgetInfo.screen = c.getInt(screenIndex);
appWidgetInfo.cellX = c.getInt(cellXIndex);
appWidgetInfo.cellY = c.getInt(cellYIndex);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
appWidgetInfo.minSpanX = minSpan[0];
appWidgetInfo.minSpanY = minSpan[1];
container = c.getInt(containerIndex);
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP
&& container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Log.e(TAG, "Widget found where container != "
+ "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
continue;
}
appWidgetInfo.container = c.getInt(containerIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, appWidgetInfo)) {
break;
}
sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
sBgAppWidgets.add(appWidgetInfo);
}
break;
}
} catch (Exception e) {
Log.w(TAG, "Desktop items loading interrupted:", e);
}
}
} finally {
c.close();
}
if (itemsToRemove.size() > 0) {
ContentProviderClient client = contentResolver
.acquireContentProviderClient(LauncherSettings.Favorites.CONTENT_URI);
// Remove dead items
for (long id : itemsToRemove) {
if (DEBUG_LOADERS) {
Log.d(TAG, "Removed id = " + id);
}
// Don't notify content observers
try {
client.delete(LauncherSettings.Favorites.getContentUri(id, false), null, null);
} catch (RemoteException e) {
Log.w(TAG, "Could not remove id = " + id);
}
}
}
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis() - t) + "ms");
Log.d(TAG, "workspace layout: ");
for (int y = 0; y < mCellCountY; y++) {
String line = "";
for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
if (s > 0) {
line += " | ";
}
for (int x = 0; x < mCellCountX; x++) {
line += ((occupied[s][x][y] != null) ? "#" : ".");
}
}
Log.d(TAG, "[ " + line + " ]");
}
}
}
}
然后在执行的过程中,按照各自的时机去调用Callback接口的一系列方法
接下来就是Activity了
Launcher.java 实现了LauncherModel接口,并且重写的LauncherModel.Callback的所有方法。在相应的方法中就可以取到数据了。