launcher5.0-资源加载
桌面启动
执行LauncherApplication.java ----onCreate()方法
@Override public void onCreate() { super.onCreate(); // set sIsScreenXLarge and sScreenDensity *before* creating icon cache sIsScreenLarge = getResources().getBoolean(R.bool.is_large_screen); sScreenDensity = getResources().getDisplayMetrics().density; recreateWidgetPreviewDb(); mIconCache = new IconCache(this); mModel = new LauncherModel(this, mIconCache); LauncherApps launcherApps = (LauncherApps) getSystemService(Context.LAUNCHER_APPS_SERVICE); launcherApps.registerCallback(mModel.getLauncherAppsCallback()); // Register intent receivers IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); registerReceiver(mModel, filter); filter = new IntentFilter(); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); registerReceiver(mModel, filter); filter = new IntentFilter(); filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); registerReceiver(mModel, filter); // Register for changes to the favorites ContentResolver resolver = getContentResolver(); resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mFavoritesObserver); }
注册 favorites observer
/** * Receives notifications whenever the user favorites have changed. */ private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { // If the database has ever changed, then we really need to force a reload of the // workspace on the next load mModel.resetLoadedState(false, true); mModel.startLoaderFromBackground(); } };
执行 mModel.startLoaderFromBackground();
/** * When the launcher is in the background, it's possible for it to miss paired * configuration changes. So whenever we trigger the loader from the background * tell the launcher that it needs to re-run the loader when it comes back instead * of doing it now. */ public void startLoaderFromBackground() { boolean runLoader = false; if (mCallbacks != null) { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { // Only actually run the loader if they're not paused. if (!callbacks.setLoadOnResume()) { runLoader = true; } } } if (runLoader) { startLoader(false, -1); } }
执行 startLoader(false, -1);
public void startLoader(boolean isLaunching, int synchronousBindPage) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // Clear any deferred bind-runnables from the synchronized load process // We must do this before any loading/binding is scheduled below. mDeferredBindRunnables.clear(); // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { // If there is already one running, tell it to stop. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp, isLaunching); if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } else { sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }
如上加载资源执行mLoaderTask ,mLoaderTask implements Runnable 方法 看下mLoaderTask run
public void run() { synchronized (mLock) { mIsLoaderTaskRunning = true; } keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } // First step. Load workspace first, this is necessary since adding of apps from // managed profile in all apps is deferred until onResume. See http://b/17336902. if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); loadAndBindWorkspace(); if (mStopped) { break keep_running; } // Whew! Hard work done. Slow us down, and wait until the UI thread has // settled down. synchronized (mLock) { if (mIsLaunching) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } waitForIdle(); // Second step. Load all apps. if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); // Restore the default thread priority after we are done loading items synchronized (mLock) { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } } // Update the saved icons if necessary if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); synchronized (sBgLock) { for (Object key : sBgDbIconCache.keySet()) { updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); } sBgDbIconCache.clear(); } // Clear out this reference, otherwise we end up holding it until all of the // callback runnables are done. mContext = null; synchronized (mLock) { // If we are still the last one to be scheduled, remove ourselves. if (mLoaderTask == this) { mLoaderTask = null; } mIsLoaderTaskRunning = false; } }
loadAndBindWorkspace()方法是加载和绑定桌面资源 (从资源文件和旧偏好)
loadAndBindAllApps()桌面绑定app信息
private void loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; // Load the workspace if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } if (!mWorkspaceLoaded) { loadWorkspace(); synchronized (LoaderTask.this) { if (mStopped) { return; } mWorkspaceLoaded = true; } } // Bind the workspace bindWorkspace(-1); }
loadWorkspace()加载桌面资源
bindWorkspace()绑定资源到桌面
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, false); 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 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 profileIdIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.PROFILE_ID); //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; UserHandle user; 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); int serialNumber = c.getInt(profileIdIndex); user = mUserManager.getUserForSerialNumber(serialNumber); // If the user doesn't exist anymore, skip. if (user == null) { itemsToRemove.add(c.getLong(idIndex)); continue; } try { intent = Intent.parseUri(intentDescription, 0); } catch (URISyntaxException e) { continue; } if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { info = getShortcutInfo( manager, intent, user, context, c, iconIndex, titleIndex, mLabelCache); } else { info = getShortcutInfo(c, context, iconTypeIndex, iconPackageIndex, iconResourceIndex, iconIndex, titleIndex); // 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); info.intent.putExtra(ItemInfo.EXTRA_PROFILE, info.user); // 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 + " ]"); } } } }
mApp.getLauncherProvider().loadDefaultFavoritesIfNecessary(0, false); 从资源default_workspace.xml中读取默认的偏好到默认favorite数据库中,即初始化favorite,然后获取数据库信息解析分类加载到内存
看下初始化favorite
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_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; }
需要解析default_workspace.xml内容如下:
<favorites xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher5"> <!-- Far-left screen [0] --> <!-- Left screen [1] --> <appwidget launcher:packageName="com.android.settings" launcher:className="com.android.settings.widget.SettingsAppWidgetProvider" launcher:screen="1" launcher:x="0" launcher:y="3" launcher:spanX="4" launcher:spanY="1" /> <!-- Middle screen [2] --> <appwidget launcher:packageName="com.android.deskclock" launcher:className="com.android.alarmclock.AnalogAppWidgetProvider" launcher:screen="2" launcher:x="1" launcher:y="0" launcher:spanX="2" launcher:spanY="2" /> <favorite launcher:packageName="com.android.camera2" launcher:className="com.android.camera.CameraLauncher" launcher:screen="2" launcher:x="0" launcher:y="3" /> <!-- Right screen [3] --> <favorite launcher:packageName="com.android.gallery3d" launcher:className="com.android.gallery3d.app.Gallery" launcher:screen="3" launcher:x="1" launcher:y="3" /> <favorite launcher:packageName="com.android.settings" launcher:className="com.android.settings.Settings" launcher:screen="3" launcher:x="2" launcher:y="3" /> <!-- Far-right screen [4] --> <!-- Hotseat (We use the screen as the position of the item in the hotseat) --> <favorite launcher:packageName="com.android.dialer" launcher:className="com.android.dialer.DialtactsActivity" launcher:container="-101" launcher:screen="0" launcher:x="0" launcher:y="0" /> <favorite launcher:packageName="com.android.contacts" launcher:className="com.android.contacts.activities.PeopleActivity" launcher:container="-101" launcher:screen="1" launcher:x="1" launcher:y="0" /> <favorite launcher:packageName="com.android.mms" launcher:className="com.android.mms.ui.ConversationList" launcher:container="-101" launcher:screen="3" launcher:x="3" launcher:y="0" /> <favorite launcher:packageName="com.android.browser" launcher:className="com.android.browser.BrowserActivity" launcher:container="-101" launcher:screen="4" launcher:x="4" launcher:y="0" /> </favorites>
解析:
<appwidget // widget launcher:packageName="com.android.settings" // pkg launcher:className="com.android.settings.widget.SettingsAppWidgetProvider" // 启动actName launcher:screen="1" //第几屏 launcher:x="0" //行 launcher:y="3" //列 launcher:spanX="4" //x占格 launcher:spanY="1" /> // y占格
<favorite // 快捷方式 launcher:packageName="com.android.browser" launcher:className="com.android.browser.BrowserActivity" launcher:container="-101"//底部快捷方式 launcher:screen="4" launcher:x="4" launcher:y="0" />
//除了appwidget favorite 还有如下
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
private static final String TAG_CLOCK = "clock";
private static final String TAG_SEARCH = "search";
private static final String TAG_APPWIDGET = "appwidget";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_FOLDER = "folder";
private static final String TAG_EXTRA = "extra";
到此loadWorkspace()资源加载完。
workspace信息加载后分布如下几个集合中:
sBgItemsIdmap: map<id,iteminfo> 所有元素 包括 桌面快捷方式,底部栏快捷方式,文件夹,小控件
sBgWorkspaceItems: List 括 桌面快捷方式,底部栏快捷方式,
sBgAppWidgets: List 小控件
sBgFolders: hashmap<folderId,folderItem> 文件夹
接下来研究绑定
/** * Binds all loaded data to actual views on the main thread. */ private void bindWorkspace(int synchronizeBindPage) { final long t = SystemClock.uptimeMillis(); Runnable r; // Don't use these two variables in any of the callback runnables. // Otherwise we hold a reference to them. final Callbacks oldCallbacks = mCallbacks.get(); if (oldCallbacks == null) { // This launcher has exited and nobody bothered to tell us. Just bail. Log.w(TAG, "LoaderTask running with no launcher"); return; } final boolean isLoadingSynchronously = (synchronizeBindPage > -1); final int currentScreen = isLoadingSynchronously ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen(); // Load all the items that are on the current page first (and in the process, unbind // all the existing workspace items before we call startBinding() below. unbindWorkspaceItemsOnMainThread(); ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<LauncherAppWidgetInfo>(); HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>(); HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>(); synchronized (sBgLock) { workspaceItems.addAll(sBgWorkspaceItems); appWidgets.addAll(sBgAppWidgets); folders.putAll(sBgFolders); itemsIdMap.putAll(sBgItemsIdMap); } ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>(); HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>(); // Separate the items that are on the current screen, and all the other remaining items filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets, otherAppWidgets); filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders, otherFolders); sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems); // Tell the workspace that we're about to start binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.startBinding(); } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); // Load items on the current page bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null); if (isLoadingSynchronously) { r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.onPageBoundSynchronously(currentScreen); } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } // Load all the remaining pages (if we are loading synchronously, we want to defer this // work until after the first render) mDeferredBindRunnables.clear(); bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, (isLoadingSynchronously ? mDeferredBindRunnables : null)); // Tell the workspace that we're done binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.finishBindingItems(); } // If we're profiling, ensure this is the last thing in the queue. if (DEBUG_LOADERS) { Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); } mIsLoadingAndBindingWorkspace = false; } }; if (isLoadingSynchronously) { mDeferredBindRunnables.add(r); } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } }
bindWorkspaceItems
绑定当前页面,绑定其他页
private void bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList<ItemInfo> workspaceItems, final ArrayList<LauncherAppWidgetInfo> appWidgets, final HashMap<Long, FolderInfo> folders, ArrayList<Runnable> deferredBindRunnables) { final boolean postOnMainThread = (deferredBindRunnables != null); // Bind the workspace items int N = workspaceItems.size(); for (int i = 0; i < N; i += ITEMS_CHUNK) { final int start = i; final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); final Runnable r = new Runnable() { @Override public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindItems(workspaceItems, start, start+chunkSize); } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } } // Bind the folders if (!folders.isEmpty()) { final Runnable r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindFolders(folders); } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } } // Bind the widgets, one at a time N = appWidgets.size(); for (int i = 0; i < N; i++) { final LauncherAppWidgetInfo widget = appWidgets.get(i); final Runnable r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAppWidget(widget); } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } } }
private static final int ITEMS_CHUNK = 6;
绑定顺序:
绑定快捷方式:callbacks.bindItems(workspaceItems, start, start+chunkSize);
绑定文件夹:callbacks.bindFolders(folders);
绑定小控件:callbacks.bindAppWidget(widget);
再下来研究具体绑定函数:Launcher。java 中的bindItems方式的实现
/** * Bind the items start-end from the list. * * Implementation of the method from LauncherModel.Callbacks. */ public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end) { if (waitUntilResume(new Runnable() { public void run() { bindItems(shortcuts, start, end); } })) { return; } // Get the list of added shortcuts and intersect them with the set of shortcuts here Set<String> newApps = new HashSet<String>(); newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps); Workspace workspace = mWorkspace; for (int i = start; i < end; i++) { final ItemInfo item = shortcuts.get(i); // Short circuit if we are loading dock items for a configuration which has no dock if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && mHotseat == null) { continue; } switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ShortcutInfo info = (ShortcutInfo) item; String uri = info.intent.toUri(0).toString(); View shortcut = createShortcut(info); workspace.addInScreen(shortcut, item.container, item.screen, item.cellX, item.cellY, 1, 1, false); boolean animateIconUp = false; synchronized (newApps) { if (newApps.contains(uri)) { animateIconUp = newApps.remove(uri); } } if (animateIconUp) { // Prepare the view to be animated up shortcut.setAlpha(0f); shortcut.setScaleX(0f); shortcut.setScaleY(0f); mNewShortcutAnimatePage = item.screen; if (!mNewShortcutAnimateViews.contains(shortcut)) { mNewShortcutAnimateViews.add(shortcut); } } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), (FolderInfo) item, mIconCache); workspace.addInScreen(newFolder, item.container, item.screen, item.cellX, item.cellY, 1, 1, false); break; } } workspace.requestLayout(); }
waitUntilResume函数暂不看 在pause情况下才执行
快捷方式和应用图标,文件夹生成相应View后加入workspace,具体如何加入view 待分析自定义workspace控件再看。
mNewShortcutAnimateViews 在绑定完成后统一执行动画显示 如下:
public void finishBindingItems() { if (waitUntilResume(new Runnable() { public void run() { finishBindingItems(); } })) { return; } if (mSavedState != null) { if (!mWorkspace.hasFocus()) { mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus(); } mSavedState = null; } mWorkspace.restoreInstanceStateForRemainingPages(); // If we received the result of any pending adds while the loader was running (e.g. the // widget configuration forced an orientation change), process them now. for (int i = 0; i < sPendingAddList.size(); i++) { completeAdd(sPendingAddList.get(i)); } sPendingAddList.clear(); // Update the market app icon as necessary (the other icons will be managed in response to // package changes in bindSearchablesChanged() updateAppMarketIcon(); // Animate up any icons as necessary if (mVisible || mWorkspaceLoading) { Runnable newAppsRunnable = new Runnable() { @Override public void run() { runNewAppsAnimation(false); } }; boolean willSnapPage = mNewShortcutAnimatePage > -1 && mNewShortcutAnimatePage != mWorkspace.getCurrentPage(); if (canRunNewAppsAnimation()) { // If the user has not interacted recently, then either snap to the new page to show // the new-apps animation or just run them if they are to appear on the current page if (willSnapPage) { mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable); } else { runNewAppsAnimation(false); } } else { // If the user has interacted recently, then just add the items in place if they // are on another page (or just normally if they are added to the current page) runNewAppsAnimation(willSnapPage); } } mWorkspaceLoading = false; }
/** * Runs a new animation that scales up icons that were added while Launcher was in the * background. * * @param immediate whether to run the animation or show the results immediately */ private void runNewAppsAnimation(boolean immediate) { AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); Collection<Animator> bounceAnims = new ArrayList<Animator>(); // Order these new views spatially so that they animate in order Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() { @Override public int compare(View a, View b) { CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams(); CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams(); int cellCountX = LauncherModel.getCellCountX(); return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX); } }); // Animate each of the views in place (or show them immediately if requested) if (immediate) { for (View v : mNewShortcutAnimateViews) { v.setAlpha(1f); v.setScaleX(1f); v.setScaleY(1f); } } else { for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) { View v = mNewShortcutAnimateViews.get(i); ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v, PropertyValuesHolder.ofFloat("alpha", 1f), PropertyValuesHolder.ofFloat("scaleX", 1f), PropertyValuesHolder.ofFloat("scaleY", 1f)); bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator()); bounceAnims.add(bounceAnim); } anim.playTogether(bounceAnims); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mWorkspace != null) { mWorkspace.postDelayed(mBuildLayersRunnable, 500); } } }); anim.start(); } // Clean up mNewShortcutAnimatePage = -1; mNewShortcutAnimateViews.clear(); new Thread("clearNewAppsThread") { public void run() { mSharedPrefs.edit() .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1) .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null) .commit(); } }.start(); }
自此bindWorkspaceItems()完结
则loadAndBindWorkspace()方法是加载和绑定桌面资源 (从资源文件和旧偏好)
开始分析 loadAndBindAllApps()桌面绑定app信息
private void loadAndBindAllApps() { if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded); } if (!mAllAppsLoaded) { loadAllAppsByBatch(); synchronized (LoaderTask.this) { if (mStopped) { return; } mAllAppsLoaded = true; } } else { onlyBindAllApps(); } }
如上 执行loadAllAppsByBatch();
private void loadAllAppsByBatch() { final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; // Don't use these two variables in any of the callback runnables. // Otherwise we hold a reference to them. final Callbacks oldCallbacks = mCallbacks.get(); if (oldCallbacks == null) { // This launcher has exited and nobody bothered to tell us. Just bail. Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)"); return; } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final List<UserHandle> profiles = mUserManager.getUserProfiles(); mBgAllAppsList.clear(); final int profileCount = profiles.size(); for (int p = 0; p < profileCount; p++) { UserHandle user = profiles.get(p); List<LauncherActivityInfo> apps = null; int N = Integer.MAX_VALUE; int startIndex; int i = 0; int batchSize = -1; while (i < N && !mStopped) { if (i == 0) { final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; apps = mLauncherApps.getActivityList(null, user); if (DEBUG_LOADERS) { Log.d(TAG, "queryIntentActivities took " + (SystemClock.uptimeMillis()-qiaTime) + "ms"); } if (apps == null) { return; } N = apps.size(); if (DEBUG_LOADERS) { Log.d(TAG, "queryIntentActivities got " + N + " apps"); } if (N == 0) { // There are no apps?!? return; } if (mBatchSize == 0) { batchSize = N; } else { batchSize = mBatchSize; } final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; Collections.sort(apps, new LauncherModel.ShortcutNameComparator(mLabelCache)); if (DEBUG_LOADERS) { Log.d(TAG, "sort took " + (SystemClock.uptimeMillis()-sortTime) + "ms"); } } final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; startIndex = i; for (int j=0; i<N && j<batchSize; j++) { // This builds the icon bitmaps. mBgAllAppsList.add(new ApplicationInfo(apps.get(i), user, mIconCache, mLabelCache)); i++; } final Callbacks callbacks = tryGetCallbacks(oldCallbacks); final ArrayList<ApplicationInfo> added = mBgAllAppsList.added; final boolean firstProfile = p == 0; mBgAllAppsList.added = new ArrayList<ApplicationInfo>(); mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (firstProfile) { callbacks.bindAllApplications(added); } else { callbacks.bindAppsAdded(added); } if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in " + (SystemClock.uptimeMillis() - t) + "ms"); } } else { Log.i(TAG, "not binding apps: no Launcher activity"); } } }); if (DEBUG_LOADERS) { Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in " + (SystemClock.uptimeMillis()-t2) + "ms"); } if (mAllAppsLoadDelay > 0 && i < N) { try { if (DEBUG_LOADERS) { Log.d(TAG, "sleeping for " + mAllAppsLoadDelay + "ms"); } Thread.sleep(mAllAppsLoadDelay); } catch (InterruptedException exc) { } } } if (DEBUG_LOADERS) { Log.d(TAG, "cached all " + N + " apps in " + (SystemClock.uptimeMillis()-t) + "ms" + (mAllAppsLoadDelay > 0 ? " (including delay)" : "")); } } }
apps = mLauncherApps.getActivityList(null, user);
mBgAllAppsList.add(new ApplicationInfo(apps.get(i), user,
mIconCache, mLabelCache));
mBgAllAppsList 为所有app
回调 Laucher.java 中 callbacks.bindAllApplications(added);方法
/** * Add the icons for all apps. * * Implementation of the method from LauncherModel.Callbacks. */ public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { Runnable setAllAppsRunnable = new Runnable() { public void run() { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); } } }; // Remove the progress bar entirely; we could also make it GONE // but better to remove it since we know it's not going to be used View progressBar = mAppsCustomizeTabHost. findViewById(R.id.apps_customize_progress_bar); if (progressBar != null) { ((ViewGroup)progressBar.getParent()).removeView(progressBar); // We just post the call to setApps so the user sees the progress bar // disappear-- otherwise, it just looks like the progress bar froze // which doesn't look great mAppsCustomizeTabHost.post(setAllAppsRunnable); } else { // If we did not initialize the spinner in onCreate, then we can directly set the // list of applications without waiting for any progress bars views to be hidden. setAllAppsRunnable.run(); } }
AppsCustomizePagedView为所有app界面
mAppsCustomizeContent.setApps(apps);所有app数据表示绑定所有app界面完成
自此 loadAndBindAllApps()桌面绑定app信息完结
即launcher资源加载全部完成了!
参考了:http://blog.csdn.net/wdaming1986/article/details/7585649