usb bundle和package区别和desktop mis的区别

今天看啥 热点:
Handler与HandlerThread区别,HandlerThread应用(对比AsyncTask),asynctaskhandler
跟道歉,之前说的一个观点有误,下面已经跟正!
之前说过HandlerThread的原理::
Handler机制的分发中心就在Looper中的loop(),HandlerThread将loop转到子线程中处理,降低了主线程的压力,使主界面更流畅
&&&其实说白了,创建HandlerThread,是为了用此线程的looper &还有异步操作最终的runnable是在子线程中运行的但是却可以Toast(不能跟新UI,却可以进行Toast,已用Toast测试过)
这里我以Launcher2的代码来说说HandlerThread的具体应用::
先说下下面这个类的分析结果:
& & & &运用了两个Handler跟两个Loop,DeferredHandler使用的是主线程中的Loop,sWorker使用的是HandlerThread中的Loop。
& & & &有网友做过测试Handler效率要比HandlerThread效率高3倍,我做的测试很奇怪,是使用HandlerThread的Loop的Runnable也会在子线程中运行(跟一位网友测试结果相同),所以不能跟新UI,但是却可以进行Toast
& & & &下面的Launcher2代码中HandlerThread中一般都是在做ContentProvider的增删改查,而Handler一般都是用来跟新主界面。
& & & &使用HandlerThread进行异步数据操作会减少主线程的消耗,使用Handler跟新界面,会使界面快速响应,使用顺畅。
& & & &在这篇文章的最后我为大家提供了一个AsyncTask的管理类,这里就没有疑问了,这里使用HandlerThread跟AsyncTask同属于子线程在进行数据库的增删改查的时候都是有效的!
* Copyright (C) 2008 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the &License&);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an &AS IS& BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
package com.android.launcher2;
import android.app.SearchM
import android.appwidget.AppWidgetM
import android.appwidget.AppWidgetProviderI
import android.content.BroadcastR
import ponentN
import android.content.ContentProviderC
import android.content.ContentR
import android.content.ContentV
import android.content.C
import android.content.I
import android.content.Intent.ShortcutIconR
import android.content.pm.ActivityI
import android.content.pm.PackageM
import android.content.pm.ResolveI
import android.content.res.C
import android.content.res.R
import android.database.C
import android.graphics.B
import android.graphics.BitmapF
import android.net.U
import android.os.E
import android.os.H
import android.os.HandlerT
import android.os.P
import android.os.P
import android.os.RemoteE
import android.os.SystemC
import android.util.L
import com.android.launcher.R;
import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerD
import java.lang.ref.WeakR
import java.net.URISyntaxE
import java.text.C
import java.util.ArrayL
import java.util.C
import java.util.HashM
import java.util.L
import java.util.L
* Maintains in-memory state of the Launcher. It is expected that there should be only one
* LauncherModel object held in a static. Also provide APIs for updating the database state
* for the Launcher.
public class LauncherModel extends BroadcastReceiver {
static final boolean DEBUG_LOADERS =
static final String TAG = &Launcher.Model&;
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private final boolean mAppsCanBeOnExternalS
private int mBatchS // 0 is all apps at once
private int mAllAppsLoadD // milliseconds between batches
private final LauncherApplication mA
private final Object mLock = new Object();
private DeferredHandler mHandler = new DeferredHandler();
private LoaderTask mLoaderT
// Handler机制的分发中心就在Looper中的loop(),HandlerThread将loop转到子线程中处理,降低了主线程的压力,使主界面更流畅
private static final HandlerThread sWorkerThread = new HandlerThread(&launcher-loader&);
sWorkerThread.start();
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
// We start off with everything not loaded.
After that, we assume that
// our monitoring of the package manager provides all updates and we never
// need to do a requery.
These are only ever touched from the loader thread.
private boolean mWorkspaceL
private boolean mAllAppsL
private WeakReference&Callbacks& mC
// & only access in worker thread &
private AllAppsList mAllAppsL
// sItemsIdMap maps *all* the ItemInfos (shortcuts, folders, and widgets) created by
// LauncherModel to their ids
static final HashMap&Long, ItemInfo& sItemsIdMap = new HashMap&Long, ItemInfo&();
// sItems is passed to bindItems, which expects a list of all folders and shortcuts created by
LauncherModel that are directly on the home screen (however, no widgets or shortcuts
within folders).
static final ArrayList&ItemInfo& sWorkspaceItems = new ArrayList&ItemInfo&();
// sAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
static final ArrayList&LauncherAppWidgetInfo& sAppWidgets =
new ArrayList&LauncherAppWidgetInfo&();
// sFolders is all FolderInfos created by LauncherModel. Passed to bindFolders()
static final HashMap&Long, FolderInfo& sFolders = new HashMap&Long, FolderInfo&();
// sDbIconCache is the set of ItemInfos that need to have their icons updated in the database
static final HashMap&Object, byte[]& sDbIconCache = new HashMap&Object, byte[]&();
// &/ only access in worker thread &
private IconCache mIconC
private Bitmap mDefaultI
private static int mCellCountX;
private static int mCellCountY;
protected int mPreviousConfigM
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&ApplicationInfo& apps, boolean permanent);
public void bindPackagesUpdated();
public boolean isAllAppsVisible();
public void bindSearchablesChanged();
LauncherModel(LauncherApplication app, IconCache iconCache) {
mAppsCanBeOnExternalStorage = !Environment.isExternalStorageEmulated();
mAllAppsList = new AllAppsList(iconCache);
mIconCache = iconC
mDefaultIcon = Utilities.createIconBitmap(
// 默认图片
mIconCache.getFullResDefaultActivityIcon(), app);
final Resources res = app.getResources();
// 延时load
mAllAppsLoadDelay = res.getInteger(R.integer.config_allAppsBatchLoadDelay);
// batchSize
mBatchSize = res.getInteger(R.integer.config_allAppsBatchSize);
// 设备配置
Configuration config = res.getConfiguration();
mPreviousConfigMcc = config.
public Bitmap getFallbackIcon() {
return Bitmap.createBitmap(mDefaultIcon);
public void unbindWorkspaceItems() {
sWorker.post(new Runnable() {
public void run() {
unbindWorkspaceItemsOnMainThread();
/** Unbinds all the sWorkspaceItems on the main thread, and return a copy of sWorkspaceItems
* that is save to reference from the main thread. */
private ArrayList&ItemInfo& unbindWorkspaceItemsOnMainThread() {
// Ensure that we don't use the same workspace items data structure on the main thread
// by making a copy of workspace items first.
final ArrayList&ItemInfo& workspaceItems = new ArrayList&ItemInfo&(sWorkspaceItems);
mHandler.post(new Runnable() {
public void run() {
for (ItemInfo item : workspaceItems) {
item.unbind();
return workspaceI
* Adds an item to the DB if it was not created previously, or move it to a new
* &container, screen, cellX, cellY&
static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
int screen, int cellX, int cellY) {
if (item.container == ItemInfo.NO_ID) {
// From all apps
addItemToDatabase(context, item, container, screen, cellX, cellY, false);
// From somewhere else
moveItemInDatabase(context, item, container, screen, cellX, cellY);
static void updateItemInDatabaseHelper(Context context, final ContentValues values,
final ItemInfo item, final String callingFunction) {
final long itemId = item.
final Uri uri = LauncherSettings.Favorites.getContentUri(itemId, false);
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
public void run() {
cr.update(uri, values, null, null);
ItemInfo modelItem = sItemsIdMap.get(itemId);
if (item != modelItem) {
// the modelItem needs to match up perfectly with item if our model is to be
// consistent with the database-- for now, just require modelItem == item
String msg = &item: & + ((item != null) ? item.toString() : &null&) +
&modelItem: & + ((modelItem != null) ? modelItem.toString() : &null&) +
&Error: ItemInfo passed to & + callingFunction + & doesn't match original&;
throw new RuntimeException(msg);
// Items are added/removed from the corresponding FolderInfo elsewhere, such
// as in Workspace.onDrop. Here, we just add/remove them from the list of items
// that are on the desktop, as appropriate
if (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
if (!sWorkspaceItems.contains(modelItem)) {
sWorkspaceItems.add(modelItem);
sWorkspaceItems.remove(modelItem);
if (sWorkerThread.getThreadId() == Process.myTid()) {
sWorker.post(r);
* Move an item in the DB to a new &container, screen, cellX, cellY&
static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
final int screen, final int cellX, final int cellY) {
item.container =
item.cellX = cellX;
item.cellY = cellY;
// We store hotseat items in canonical form which is this orientation invariant position
// in the hotseat
if (context instanceof Launcher && screen & 0 &&
container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
item.screen =
final ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.CONTAINER, item.container);
values.put(LauncherSettings.Favorites.CELLX, item.cellX);
values.put(LauncherSettings.Favorites.CELLY, item.cellY);
values.put(LauncherSettings.Favorites.SCREEN, item.screen);
updateItemInDatabaseHelper(context, values, item, &moveItemInDatabase&);
* Resize an item in the DB to a new &spanX, spanY, cellX, cellY&
static void resizeItemInDatabase(Context context, final ItemInfo item, final int cellX,
final int cellY, final int spanX, final int spanY) {
item.spanX = spanX;
item.spanY = spanY;
item.cellX = cellX;
item.cellY = cellY;
final ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.CONTAINER, item.container);
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
values.put(LauncherSettings.Favorites.CELLX, cellX);
values.put(LauncherSettings.Favorites.CELLY, cellY);
updateItemInDatabaseHelper(context, values, item, &resizeItemInDatabase&);
* Update an item to the database in a specified container.
static void updateItemInDatabase(Context context, final ItemInfo item) {
final ContentValues values = new ContentValues();
item.onAddToDatabase(values);
item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
updateItemInDatabaseHelper(context, values, item, &updateItemInDatabase&);
* Returns true if the shortcuts already exists in the database.
* we identify a shortcut by its title and intent.
static boolean shortcutExists(Context context, String title, Intent intent) {
final ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
new String[] { &title&, &intent& }, &title=? and intent=?&,
new String[] { title, intent.toUri(0) }, null);
boolean result =
result = c.moveToFirst();
} finally {
c.close();
* Returns an ItemInfo array containing all the items in the LauncherModel.
* The ItemInfo.id is not set through this function.
static ArrayList&ItemInfo& getItemsInLocalCoordinates(Context context) {
ArrayList&ItemInfo& items = new ArrayList&ItemInfo&();
final ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
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);
while (c.moveToNext()) {
ItemInfo item = new ItemInfo();
item.cellX = c.getInt(cellXIndex);
item.cellY = c.getInt(cellYIndex);
item.spanX = c.getInt(spanXIndex);
item.spanY = c.getInt(spanYIndex);
item.container = c.getInt(containerIndex);
item.itemType = c.getInt(itemTypeIndex);
item.screen = c.getInt(screenIndex);
items.add(item);
} catch (Exception e) {
items.clear();
} finally {
c.close();
* Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
FolderInfo getFolderById(Context context, HashMap&Long,FolderInfo& folderList, long id) {
final ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
&_id=? and (itemType=? or itemType=?)&,
new String[] { String.valueOf(id),
String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
if (c.moveToFirst()) {
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
FolderInfo folderInfo =
switch (c.getInt(itemTypeIndex)) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folderInfo = findOrMakeFolder(folderList, id);
folderInfo.title = c.getString(titleIndex);
folderInfo.id =
folderInfo.container = c.getInt(containerIndex);
folderInfo.screen = c.getInt(screenIndex);
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
return folderI
} finally {
c.close();
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
static void addItemToDatabase(Context context, final ItemInfo item, final long container,
final int screen, final int cellX, final int cellY, final boolean notify) {
item.container =
item.cellX = cellX;
item.cellY = cellY;
// We store hotseat items in canonical form which is this orientation invariant position
// in the hotseat
if (context instanceof Launcher && screen & 0 &&
container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
item.screen = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
item.screen =
final ContentValues values = new ContentValues();
final ContentResolver cr = context.getContentResolver();
item.onAddToDatabase(values);
LauncherApplication app = (LauncherApplication) context.getApplicationContext();
item.id = app.getLauncherProvider().generateNewId();
values.put(LauncherSettings.Favorites._ID, item.id);
item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
Runnable r = new Runnable() {
public void run() {
cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
if (sItemsIdMap.containsKey(item.id)) {
// we should not be adding new items in the db with the same id
throw new RuntimeException(&Error: ItemInfo id (& + item.id + &) passed to & +
&addItemToDatabase already exists.& + item.toString());
sItemsIdMap.put(item.id, item);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
sFolders.put(item.id, (FolderInfo) item);
// Fall through
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
sWorkspaceItems.add(item);
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sAppWidgets.add((LauncherAppWidgetInfo) item);
if (sWorkerThread.getThreadId() == Process.myTid()) {
sWorker.post(r);
* Creates a new unique child id, for a given cell span across all layouts.
static int getCellLayoutChildId(
long container, int screen, int localCellX, int localCellY, int spanX, int spanY) {
return (((int) container & 0xFF) && 24)
| (screen & 0xFF) && 16 | (localCellX & 0xFF) && 8 | (localCellY & 0xFF);
static int getCellCountX() {
return mCellCountX;
static int getCellCountY() {
return mCellCountY;
* Updates the model orientation helper to take into account the current layout dimensions
* when performing local/canonical coordinate transformations.
static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) {
mCellCountX = shortAxisCellC
mCellCountY = longAxisCellC
* Removes the specified item from the database
* @param context
* @param item
static void deleteItemFromDatabase(Context context, final ItemInfo item) {
final ContentResolver cr = context.getContentResolver();
final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
Runnable r = new Runnable() {
public void run() {
cr.delete(uriToDelete, null, null);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
sFolders.remove(item.id);
sWorkspaceItems.remove(item);
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
sWorkspaceItems.remove(item);
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sAppWidgets.remove((LauncherAppWidgetInfo) item);
sItemsIdMap.remove(item.id);
sDbIconCache.remove(item);
if (sWorkerThread.getThreadId() == Process.myTid()) {
sWorker.post(r);
* Remove the contents of the specified folder from the database
static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
public void run() {
cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
sItemsIdMap.remove(info.id);
sFolders.remove(info.id);
sDbIconCache.remove(info);
sWorkspaceItems.remove(info);
cr.delete(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
LauncherSettings.Favorites.CONTAINER + &=& + info.id, null);
for (ItemInfo childInfo : info.contents) {
sItemsIdMap.remove(childInfo.id);
sDbIconCache.remove(childInfo);
if (sWorkerThread.getThreadId() == Process.myTid()) {
sWorker.post(r);
* Set this as the current Launcher activity object for the loader.
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
mCallbacks = new WeakReference&Callbacks&(callbacks);
* Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
* ACTION_PACKAGE_CHANGED.
public void onReceive(Context context, Intent intent) {
if (DEBUG_LOADERS) Log.d(TAG, &onReceive intent=& + intent);
final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
|| Intent.ACTION_PACKAGE_REMOVED.equals(action)
|| Intent.ACTION_PACKAGE_ADDED.equals(action)) {
final String packageName = intent.getData().getSchemeSpecificPart();
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
int op = PackageUpdatedTask.OP_NONE;
if (packageName == null || packageName.length() == 0) {
// they sent us a bad intent
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
op = PackageUpdatedTask.OP_UPDATE;
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
if (!replacing) {
op = PackageUpdatedTask.OP_REMOVE;
// else, we are replacing the package, so a PACKAGE_ADDED will be sent
// later, we will update the package at this time
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
if (!replacing) {
op = PackageUpdatedTask.OP_ADD;
op = PackageUpdatedTask.OP_UPDATE;
if (op != PackageUpdatedTask.OP_NONE) {
enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
// First, schedule to add these apps back in.
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
// Then, rebind everything.
startLoaderFromBackground();
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
enqueuePackageUpdated(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNAVAILABLE, packages));
} else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
// Check if configuration change was an mcc/mnc change which would affect app resources
// and we would need to clear out the labels in all apps/workspace. Same handling as
// above for ACTION_LOCALE_CHANGED
Configuration currentConfig = context.getResources().getConfiguration();
if (mPreviousConfigMcc != currentConfig.mcc) {
Log.d(TAG, &Reload apps on config change. curr_mcc:&
+ currentConfig.mcc + & prevmcc:& + mPreviousConfigMcc);
forceReload();
// Update previousConfig
mPreviousConfigMcc = currentConfig.
} else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
if (mCallbacks != null) {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
callbacks.bindSearchablesChanged();
private void forceReload() {
synchronized (mLock) {
// Stop any existing loaders first, so they don't set mAllAppsLoaded or
// mWorkspaceLoaded to true later
stopLoaderLocked();
mAllAppsLoaded =
mWorkspaceLoaded =
// Do this here because if the launcher activity is running it will be restarted.
// If it's not running startLoaderFromBackground will merely tell it that it needs
// to reload.
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 =
if (mCallbacks != null) {
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
// Only actually run the loader if they're not paused.
if (!callbacks.setLoadOnResume()) {
runLoader =
if (runLoader) {
startLoader(mApp, false);
// If there is already a loader task running, tell it to stop.
// returns true if isLaunching() was true on the old task
private boolean stopLoaderLocked() {
boolean isLaunching =
LoaderTask oldTask = mLoaderT
if (oldTask != null) {
if (oldTask.isLaunching()) {
isLaunching =
oldTask.stopLocked();
return isL
public void startLoader(Context context, boolean isLaunching) {
synchronized (mLock) {
if (DEBUG_LOADERS) {
Log.d(TAG, &startLoader isLaunching=& + isLaunching);
// 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(context, isLaunching);
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
public void stopLoader() {
synchronized (mLock) {
if (mLoaderTask != null) {
mLoaderTask.stopLocked();
public boolean isAllAppsLoaded() {
return mAllAppsL
* Runnable for the thread that loads the contents of the launcher:
- workspace icons
- all apps icons
private class LoaderTask implements Runnable {
private Context mC
private Thread mWaitT
private boolean mIsL
private boolean mS
private boolean mLoadAndBindStepF
private HashMap&Object, CharSequence& mLabelC
LoaderTask(Context context, boolean isLaunching) {
mContext =
mIsLaunching = isL
mLabelCache = new HashMap&Object, CharSequence&();
boolean isLaunching() {
return mIsL
private void loadAndBindWorkspace() {
// Load the workspace
if (DEBUG_LOADERS) {
Log.d(TAG, &loadAndBindWorkspace mWorkspaceLoaded=& + mWorkspaceLoaded);
if (!mWorkspaceLoaded) {
loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
mWorkspaceLoaded =
// Bind the workspace
bindWorkspace();
private void waitForIdle() {
// Wait until the either we're stopped or the other threads are done.
// This way we don't start loading all apps until the workspace has settled
synchronized (LoaderTask.this) {
final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
mHandler.postIdle(new Runnable() {
public void run() {
synchronized (LoaderTask.this) {
mLoadAndBindStepFinished =
if (DEBUG_LOADERS) {
Log.d(TAG, &done with previous binding step&);
LoaderTask.this.notify();
while (!mStopped && !mLoadAndBindStepFinished) {
this.wait();
} catch (InterruptedException ex) {
if (DEBUG_LOADERS) {
Log.d(TAG, &waited &
+ (SystemClock.uptimeMillis()-workspaceWaitTime)
+ &ms for previous step to finish binding&);
public void run() {
// Optimize for end-user experience: if the Launcher is up and // running with the
// All Apps interface in the foreground, load All Apps first. Otherwise, load the
// workspace first (default).
final Callbacks cbk = mCallbacks.get();
final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) :
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&));
android.os.Process.setThreadPriority(mIsLaunching
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, &step 1: loading workspace&);
loadAndBindWorkspace();
if (DEBUG_LOADERS) Log.d(TAG, &step 1: special: loading all apps&);
loadAndBindAllApps();
if (mStopped) {
break keep_
// 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&);
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
waitForIdle();
// second step
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, &step 2: loading all apps&);
loadAndBindAllApps();
if (DEBUG_LOADERS) Log.d(TAG, &step 2: special: loading workspace&);
loadAndBindWorkspace();
// Restore the default thread priority after we are done loading items
synchronized (mLock) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
// Update the saved icons if necessary
if (DEBUG_LOADERS) Log.d(TAG, &Comparing loaded icons to database icons&);
for (Object key : sDbIconCache.keySet()) {
updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
sDbIconCache.clear();
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext =
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask =
public void stopLocked() {
synchronized (LoaderTask.this) {
mStopped =
this.notify();
* Gets the callbacks object.
If we've been stopped, or if the launcher object
* has somehow been garbage collected, return null instead.
Pass in the Callbacks
* object that was around when the deferred message was scheduled, and if there's
* a new Callbacks object around then also return null.
This will save us from
* calling onto it with data that will be ignored.
Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
synchronized (mLock) {
if (mStopped) {
if (mCallbacks == null) {
final Callbacks callbacks = mCallbacks.get();
if (callbacks != oldCallbacks) {
if (callbacks == null) {
Log.w(TAG, &no mCallbacks&);
// check & update map of what' used to discard overlapping/invalid items
private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
int containerIndex = item.
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
// Return early if we detect that an item is under the hotseat button
if (Hotseat.isAllAppsButtonRank(item.screen)) {
// We use the last index to refer to the hotseat and the screen as the rank, so
// test and update the occupied state accordingly
if (occupied[Launcher.SCREEN_COUNT][item.screen][0] != null) {
Log.e(TAG, &Error loading shortcut into hotseat & + item
+ & into position (& + item.screen + &:& + item.cellX + &,& + item.cellY
+ &) occupied by & + occupied[Launcher.SCREEN_COUNT][item.screen][0]);
occupied[Launcher.SCREEN_COUNT][item.screen][0] =
} else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
// Skip further checking if it is not the hotseat or workspace container
// Check if any workspace icons overlap with each other
for (int x = item.cellX; x & (item.cellX+item.spanX); x++) {
for (int y = item.cellY; y & (item.cellY+item.spanY); y++) {
if (occupied[containerIndex][x][y] != null) {
Log.e(TAG, &Error loading shortcut & + item
+ & into cell (& + containerIndex + &-& + item.screen + &:&
+ x + &,& + y
+ &) occupied by &
+ occupied[containerIndex][x][y]);
for (int x = item.cellX; x & (item.cellX+item.spanX); x++) {
for (int y = item.cellY; y & (item.cellY+item.spanY); y++) {
occupied[containerIndex][x][y] =
private void loadWorkspace() {
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mC
final ContentResolver contentResolver = context.getContentResolver();
final PackageManager manager = context.getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final boolean isSafeMode = manager.isSafeMode();
sWorkspaceItems.clear();
sAppWidgets.clear();
sFolders.clear();
sItemsIdMap.clear();
sDbIconCache.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];
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 uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
final int displayModeIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.DISPLAY_MODE);
String intentD
LauncherAppWidgetInfo appWidgetI
while (!mStopped && c.moveToNext()) {
int itemType = c.getInt(itemTypeIndex);
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
intentDescription = c.getString(intentIndex);
intent = Intent.parseUri(intentDescription, 0);
} catch (URISyntaxException e) {
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getShortcutInfo(manager, intent, context, c, iconIndex,
titleIndex, mLabelCache);
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
titleIndex);
if (info != null) {
info.intent =
info.id = c.getLong(idIndex);
container = c.getInt(containerIndex);
info.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)) {
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sWorkspaceItems.add(info);
// Item is in a user folder
FolderInfo folderInfo =
findOrMakeFolder(sFolders, container);
folderInfo.add(info);
sItemsIdMap.put(info.id, info);
// now that we've loaded everthing re-save it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sDbIconCache, info, c, iconIndex);
// 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);
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sFolders, id);
folderInfo.title = c.getString(titleIndex);
folderInfo.id =
container = c.getInt(containerIndex);
folderInfo.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)) {
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
sWorkspaceItems.add(folderInfo);
sItemsIdMap.put(folderInfo.id, folderInfo);
sFolders.put(folderInfo.id, folderInfo);
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);
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
appWidgetInfo.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);
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!&);
appWidgetInfo.container = c.getInt(containerIndex);
// check & update map of what's occupied
if (!checkItemPlacement(occupied, appWidgetInfo)) {
sItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
sAppWidgets.add(appWidgetInfo);
} 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
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 + & ]&);
* Read everything out of our database.
private void bindWorkspace() {
final long t = SystemClock.uptimeMillis();
// 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&);
// Tell the workspace that we're about to start firing items at it
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.startBinding();
// Unbind previously bound workspace items to prevent a leak of AppWidgetHostViews.
final ArrayList&ItemInfo& workspaceItems = unbindWorkspaceItemsOnMainThread();
// Add the items to the workspace.
N = workspaceItems.size();
for (int i=0; i&N; i+=ITEMS_CHUNK) {
final int start =
final int chunkSize = (i+ITEMS_CHUNK &= N) ? ITEMS_CHUNK : (N-i);
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindItems(workspaceItems, start, start+chunkSize);
// Ensure that we don't use the same folders data structure on the main thread
final HashMap&Long, FolderInfo& folders = new HashMap&Long, FolderInfo&(sFolders);
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindFolders(folders);
// Wait until the queue goes empty.
mHandler.post(new Runnable() {
public void run() {
if (DEBUG_LOADERS) {
Log.d(TAG, &Going to start binding widgets soon.&);
// Bind the widgets, one at a time.
// WARNING: this is calling into the workspace from the background thread,
// but since getCurrentScreen() just returns the int, we should be okay.
// is just a hint for the order, and if it's wrong, we'll be okay.
// TODO: instead, we should have that push the current screen into here.
final int currentScreen = oldCallbacks.getCurrentWorkspaceScreen();
N = sAppWidgets.size();
// once for the current screen
for (int i=0; i&N; i++) {
final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
if (widget.screen == currentScreen) {
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAppWidget(widget);
// once for the other screens
for (int i=0; i&N; i++) {
final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
if (widget.screen != currentScreen) {
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAppWidget(widget);
// Tell the workspace that we're done.
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.finishBindingItems();
// If we're profiling, this is the last thing in the queue.
mHandler.post(new Runnable() {
public void run() {
if (DEBUG_LOADERS) {
Log.d(TAG, &bound workspace in &
+ (SystemClock.uptimeMillis()-t) + &ms&);
private void loadAndBindAllApps() {
if (DEBUG_LOADERS) {
Log.d(TAG, &loadAndBindAllApps mAllAppsLoaded=& + mAllAppsLoaded);
if (!mAllAppsLoaded) {
loadAllAppsByBatch();
synchronized (LoaderTask.this) {
if (mStopped) {
mAllAppsLoaded =
onlyBindAllApps();
private void onlyBindAllApps() {
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 (onlyBindAllApps)&);
// shallow copy
final ArrayList&ApplicationInfo& list
= (ArrayList&ApplicationInfo&)mAllAppsList.data.clone();
mHandler.post(new Runnable() {
public void run() {
final long t = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(list);
if (DEBUG_LOADERS) {
Log.d(TAG, &bound all & + list.size() + & apps from cache in &
+ (SystemClock.uptimeMillis()-t) + &ms&);
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)&);
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final PackageManager packageManager = mContext.getPackageManager();
List&ResolveInfo& apps =
int N = Integer.MAX_VALUE;
int startI
int batchSize = -1;
while (i & N && !mStopped) {
if (i == 0) {
mAllAppsList.clear();
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
apps = packageManager.queryIntentActivities(mainIntent, 0);
if (DEBUG_LOADERS) {
Log.d(TAG, &queryIntentActivities took &
+ (SystemClock.uptimeMillis()-qiaTime) + &ms&);
if (apps == null) {
N = apps.size();
if (DEBUG_LOADERS) {
Log.d(TAG, &queryIntentActivities got & + N + & apps&);
if (N == 0) {
// There are no apps?!?
if (mBatchSize == 0) {
batchSize = N;
batchSize = mBatchS
final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
Collections.sort(apps,
new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
if (DEBUG_LOADERS) {
Log.d(TAG, &sort took &
+ (SystemClock.uptimeMillis()-sortTime) + &ms&);
final long t2 = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
startIndex =
for (int j=0; i&N && j&batchS j++) {
// This builds the icon bitmaps.
mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),
mIconCache, mLabelCache));
final boolean first = i &= batchS
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
final ArrayList&ApplicationInfo& added = mAllAppsList.
mAllAppsList.added = new ArrayList&ApplicationInfo&();
mHandler.post(new Runnable() {
public void run() {
final long t = SystemClock.uptimeMillis();
if (callbacks != null) {
if (first) {
callbacks.bindAllApplications(added);
callbacks.bindAppsAdded(added);
if (DEBUG_LOADERS) {
Log.d(TAG, &bound & + added.size() + & apps in &
+ (SystemClock.uptimeMillis() - t) + &ms&);
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) {
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)& : &&));
public void dumpState() {
Log.d(TAG, &mLoaderTask.mContext=& + mContext);
Log.d(TAG, &mLoaderTask.mWaitThread=& + mWaitThread);
Log.d(TAG, &mLoaderTask.mIsLaunching=& + mIsLaunching);
Log.d(TAG, &mLoaderTask.mStopped=& + mStopped);
Log.d(TAG, &mLoaderTask.mLoadAndBindStepFinished=& + mLoadAndBindStepFinished);
Log.d(TAG, &mItems size=& + sWorkspaceItems.size());
void enqueuePackageUpdated(PackageUpdatedTask task) {
sWorker.post(task);
private class PackageUpdatedTask implements Runnable {
String[] mP
public static final int OP_NONE = 0;
public static final int OP_ADD = 1;
public static final int OP_UPDATE = 2;
public static final int OP_REMOVE = 3; // uninstlled
public static final int OP_UNAVAILABLE = 4; // external media unmounted
public PackageUpdatedTask(int op, String[] packages) {
mPackages =
public void run() {
final Context context = mA
final String[] packages = mP
final int N = packages.
switch (mOp) {
case OP_ADD:
for (int i=0; i&N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, &mAllAppsList.addPackage & + packages[i]);
mAllAppsList.addPackage(context, packages[i]);
case OP_UPDATE:
for (int i=0; i&N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, &mAllAppsList.updatePackage & + packages[i]);
mAllAppsList.updatePackage(context, packages[i]);
case OP_REMOVE:
case OP_UNAVAILABLE:
for (int i=0; i&N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, &mAllAppsList.removePackage & + packages[i]);
mAllAppsList.removePackage(packages[i]);
ArrayList&ApplicationInfo& added =
ArrayList&ApplicationInfo& removed =
ArrayList&ApplicationInfo& modified =
if (mAllAppsList.added.size() & 0) {
added = mAllAppsList.
mAllAppsList.added = new ArrayList&ApplicationInfo&();
if (mAllAppsList.removed.size() & 0) {
removed = mAllAppsList.
mAllAppsList.removed = new ArrayList&ApplicationInfo&();
for (ApplicationInfo info: removed) {
mIconCache.remove(info.intent.getComponent());
if (mAllAppsList.modified.size() & 0) {
modified = mAllAppsList.
mAllAppsList.modified = new ArrayList&ApplicationInfo&();
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() :
if (callbacks == null) {
Log.w(TAG, &Nobody to tell about the new app.
Launcher is probably loading.&);
if (added != null) {
final ArrayList&ApplicationInfo& addedFinal =
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() :
if (callbacks == cb && cb != null) {
callbacks.bindAppsAdded(addedFinal);
if (modified != null) {
final ArrayList&ApplicationInfo& modifiedFinal =
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() :
if (callbacks == cb && cb != null) {
callbacks.bindAppsUpdated(modifiedFinal);
if (removed != null) {
final boolean permanent = mOp != OP_UNAVAILABLE;
final ArrayList&ApplicationInfo& removedFinal =
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() :
if (callbacks == cb && cb != null) {
callbacks.bindAppsRemoved(removedFinal, permanent);
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() :
if (callbacks == cb && cb != null) {
callbacks.bindPackagesUpdated();
* This is called from the code that adds shortcuts from the intent receiver.
* doesn't have a Cursor, but
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
return getShortcutInfo(manager, intent, context, null, -1, -1, null);
* Make an ShortcutInfo object for a shortcut that is an application.
* If c is not null, then it will be used to fill in missing data like the title and icon.
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
Cursor c, int iconIndex, int titleIndex, HashMap&Object, CharSequence& labelCache) {
Bitmap icon =
final ShortcutInfo info = new ShortcutInfo();
ComponentName componentName = intent.getComponent();
if (componentName == null) {
// TODO: See if the PackageManager knows about this case.
If it doesn't
// then return null & delete this.
// the resource -- This may implicitly give us back the fallback icon,
// but don't worry about that.
All we're doing with usingFallbackIcon is
// to avoid saving lots of copies of that in the database, and most apps
// have icons anyway.
final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
if (resolveInfo != null) {
icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
if (icon == null) {
if (c != null) {
icon = getIconFromCursor(c, iconIndex, context);
// the fallback icon
if (icon == null) {
icon = getFallbackIcon();
info.usingFallbackIcon =
info.setIcon(icon);
// from the resource
if (resolveInfo != null) {
ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
if (labelCache != null && labelCache.containsKey(key)) {
info.title = labelCache.get(key);
info.title = resolveInfo.activityInfo.loadLabel(manager);
if (labelCache != null) {
labelCache.put(key, info.title);
// from the db
if (info.title == null) {
if (c != null) {
info.title =
c.getString(titleIndex);
// fall back to the class name of the activity
if (info.title == null) {
info.title = componentName.getClassName();
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
* Make an ShortcutInfo object for a shortcut that isn't an application.
private ShortcutInfo getShortcutInfo(Cursor c, Context context,
int iconTypeIndex, int iconPackageIndex, int iconResourceIndex, int iconIndex,
int titleIndex) {
Bitmap icon =
final ShortcutInfo info = new ShortcutInfo();
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
// TODO: If there's an explicit component and we can't install that, delete it.
info.title = c.getString(titleIndex);
int iconType = c.getInt(iconTypeIndex);
switch (iconType) {
case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
String packageName = c.getString(iconPackageIndex);
String resourceName = c.getString(iconResourceIndex);
PackageManager packageManager = context.getPackageManager();
info.customIcon =
// the resource
Resources resources = packageManager.getResourcesForApplication(packageName);
if (resources != null) {
final int id = resources.getIdentifier(resourceName, null, null);
icon = Utilities.createIconBitmap(
mIconCache.getFullResIcon(resources, id), context);
} catch (Exception e) {
// drop this.
we have other places to look for icons
if (icon == null) {
icon = getIconFromCursor(c, iconIndex, context);
// the fallback icon
if (icon == null) {
icon = getFallbackIcon();
info.usingFallbackIcon =
case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
icon = getIconFromCursor(c, iconIndex, context);
if (icon == null) {
icon = getFallbackIcon();
info.customIcon =
info.usingFallbackIcon =
info.customIcon =
icon = getFallbackIcon();
info.usingFallbackIcon =
info.customIcon =
info.setIcon(icon);
Bitmap getIconFromCursor(Cursor c, int iconIndex, Context context) {
if (false) {
Log.d(TAG, &getIconFromCursor app=&
+ c.getString(c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE)));
byte[] data = c.getBlob(iconIndex);
return Utilities.createIconBitmap(
BitmapFactory.decodeByteArray(data, 0, data.length), context);
} catch (Exception e) {
ShortcutInfo addShortcut(Context context, Intent data, long container, int screen,
int cellX, int cellY, boolean notify) {
final ShortcutInfo info = infoFromShortcutIntent(context, data, null);
addItemToDatabase(context, info, container, screen, cellX, cellY, notify);
* Attempts to find an AppWidgetProviderInfo that matches the given component.
AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
ComponentName component) {
List&AppWidgetProviderInfo& widgets =
AppWidgetManager.getInstance(context).getInstalledProviders();
for (AppWidgetProviderInfo info : widgets) {
if (info.provider.equals(component)) {
* Returns a list of all the widgets that can handle configuration with a particular mimeType.
List&WidgetMimeTypeHandlerData& resolveWidgetsForMimeType(Context context, String mimeType) {
final PackageManager packageManager = context.getPackageManager();
final List&WidgetMimeTypeHandlerData& supportedConfigurationActivities =
new ArrayList&WidgetMimeTypeHandlerData&();
final Intent supportsIntent =
new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
supportsIntent.setType(mimeType);
// Create a set of widget configuration components that we can test against
final List&AppWidgetProviderInfo& widgets =
AppWidgetManager.getInstance(context).getInstalledProviders();
final HashMap&ComponentName, AppWidgetProviderInfo& configurationComponentToWidget =
new HashMap&ComponentName, AppWidgetProviderInfo&();
for (AppWidgetProviderInfo info : widgets) {
configurationComponentToWidget.put(info.configure, info);
// Run through each of the intents that can handle this type of clip data, and cross
// reference them with the components that are actual configuration components
final List&ResolveInfo& activities = packageManager.queryIntentActivities(supportsIntent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : activities) {
final ActivityInfo activityInfo = info.activityI
final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
activityInfo.name);
if (configurationComponentToWidget.containsKey(infoComponent)) {
supportedConfigurationActivities.add(
new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
configurationComponentToWidget.get(infoComponent)));
return supportedConfigurationA
ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
Bitmap icon =
boolean customIcon =
ShortcutIconResource iconResource =
if (bitmap != null && bitmap instanceof Bitmap) {
icon = Utilities.createIconBitmap(new FastBitmapDrawable((Bitmap)bitmap), context);
customIcon =
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if (extra != null && extra instanceof ShortcutIconResource) {
iconResource = (ShortcutIconResource)
final PackageManager packageManager = context.getPackageManager();
Resources resources = packageManager.getResourcesForApplication(
iconResource.packageName);
final int id = resources.getIdentifier(iconResource.resourceName, null, null);
icon = Utilities.createIconBitmap(
mIconCache.getFullResIcon(resources, id), context);
} catch (Exception e) {
Log.w(TAG, &Could not load shortcut icon: & + extra);
final ShortcutInfo info = new ShortcutInfo();
if (icon == null) {
if (fallbackIcon != null) {
icon = fallbackI
icon = getFallbackIcon();
info.usingFallbackIcon =
info.setIcon(icon);
info.title =
info.intent =
info.customIcon = customI
info.iconResource = iconR
boolean queueIconToBeChecked(HashMap&Object, byte[]& cache, ShortcutInfo info, Cursor c,
int iconIndex) {
// If apps can't be on SD, don't even bother.
if (!mAppsCanBeOnExternalStorage) {
// If this icon doesn't have a custom icon, check to see
// what's stored in the DB, and if it doesn't match what
// we're going to show, store what we are going to show back
// into the DB.
We do this so when we're loading, if the
// package manager can't find an icon (for example because
// the app is on SD) then we can use that instead.
if (!info.customIcon && !info.usingFallbackIcon) {
cache.put(info, c.getBlob(iconIndex));
void updateSavedIcon(Context context, ShortcutInfo info, byte[] data) {
boolean needSave =
if (data != null) {
Bitmap saved = BitmapFactory.decodeByteArray(data, 0, data.length);
Bitmap loaded = info.getIcon(mIconCache);
needSave = !saved.sameAs(loaded);
needSave =
} catch (Exception e) {
needSave =
if (needSave) {
Log.d(TAG, &going to save icon bitmap for info=& + info);
// This is slower than is ideal, but this only happens once
// or when the app is updated with a new icon.
updateItemInDatabase(context, info);
* Return an existing FolderInfo object if we have encountered this ID previously,
* or make a new one.
private static FolderInfo findOrMakeFolder(HashMap&Long, FolderInfo& folders, long id) {
// See if a placeholder was created for us already
FolderInfo folderInfo = folders.get(id);
if (folderInfo == null) {
// No placeholder -- create a new instance
folderInfo = new FolderInfo();
folders.put(id, folderInfo);
return folderI
private static final Collator sCollator = Collator.getInstance();
public static final Comparator&ApplicationInfo& APP_NAME_COMPARATOR
= new Comparator&ApplicationInfo&() {
public final int compare(ApplicationInfo a, ApplicationInfo b) {
int result = pare(a.title.toString(), b.title.toString());
if (result == 0) {
result = ponentName);
public static final Comparator&ApplicationInfo& APP_INSTALL_TIME_COMPARATOR
= new Comparator&ApplicationInfo&() {
public final int compare(ApplicationInfo a, ApplicationInfo b) {
if (a.firstInstallTime & b.firstInstallTime) return 1;
if (a.firstInstallTime & b.firstInstallTime) return -1;
public static final Comparator&AppWidgetProviderInfo& WIDGET_NAME_COMPARATOR
= new Comparator&AppWidgetProviderInfo&() {
public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
pare(a.label.toString(), b.label.toString());
static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
if (info.activityInfo != null) {
return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
public static class ShortcutNameComparator implements Comparator&ResolveInfo& {
private PackageManager mPackageM
private HashMap&Object, CharSequence& mLabelC
ShortcutNameComparator(PackageManager pm) {
mPackageManager =
mLabelCache = new HashMap&Object, CharSequence&();
ShortcutNameComparator(PackageManager pm, HashMap&Object, CharSequence& labelCache) {
mPackageManager =
mLabelCache = labelC
public final int compare(ResolveInfo a, ResolveInfo b) {
CharSequence labelA, labelB;
ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
if (mLabelCache.containsKey(keyA)) {
labelA = mLabelCache.get(keyA);
labelA = a.loadLabel(mPackageManager).toString();
mLabelCache.put(keyA, labelA);
if (mLabelCache.containsKey(keyB)) {
labelB = mLabelCache.get(keyB);
labelB = b.loadLabel(mPackageManager).toString();
mLabelCache.put(keyB, labelB);
pare(labelA, labelB);
public static class WidgetAndShortcutNameComparator implements Comparator&Object& {
private PackageManager mPackageM
private HashMap&Object, String& mLabelC
WidgetAndShortcutNameComparator(PackageManager pm) {
mPackageManager =
mLabelCache = new HashMap&Object, String&();
public final int compare(Object a, Object b) {
String labelA, labelB;
if (mLabelCache.containsKey(a)) {
labelA = mLabelCache.get(a);
labelA = (a instanceof AppWidgetProviderInfo) ?
((AppWidgetProviderInfo) a).label :
((ResolveInfo) a).loadLabel(mPackageManager).toString();
mLabelCache.put(a, labelA);
if (mLabelCache.containsKey(b)) {
labelB = mLabelCache.get(b);
labelB = (b instanceof AppWidgetProviderInfo) ?
((AppWidgetProviderInfo) b).label :
((ResolveInfo) b).loadLabel(mPackageManager).toString();
mLabelCache.put(b, labelB);
pare(labelA, labelB);
public void dumpState() {
Log.d(TAG, &mCallbacks=& + mCallbacks);
ApplicationInfo.dumpApplicationInfoList(TAG, &mAllAppsList.data&, mAllAppsList.data);
ApplicationInfo.dumpApplicationInfoList(TAG, &mAllAppsList.added&, mAllAppsList.added);
ApplicationInfo.dumpApplicationInfoList(TAG, &mAllAppsList.removed&, mAllAppsList.removed);
ApplicationInfo.dumpApplicationInfoList(TAG, &mAllAppsList.modified&, mAllAppsList.modified);
if (mLoaderTask != null) {
mLoaderTask.dumpState();
Log.d(TAG, &mLoaderTask=null&);
下面的DeferredHandler有心者可以学习学习::
* Copyright (C) 2008 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the &License&);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an &AS IS& BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
package com.android.launcher2;
import java.util.LinkedL
import android.os.H
import android.os.L
import android.os.M
import android.os.MessageQ
* Queue of things to run on a looper thread.
Items posted with {@link #post} will not
* be actually enqued on the handler until after the last one has run, to keep from
* starving the thread.
* This class is fifo.
public class DeferredHandler {
private LinkedList&Runnable& mQueue = new LinkedList&Runnable&();
private MessageQueue mMessageQueue = Looper.myQueue();
private Impl mHandler = new Impl();
private class Impl extends Handler implements MessageQueue.IdleHandler {
public void handleMessage(Message msg) {
synchronized (mQueue) {
if (mQueue.size() == 0) {
r = mQueue.removeFirst();
synchronized (mQueue) {
scheduleNextLocked();
public boolean queueIdle() {
handleMessage(null);
private class IdleRunnable implements Runnable {
Runnable mR
IdleRunnable(Runnable r) {
mRunnable =
public void run() {
mRunnable.run();
public DeferredHandler() {
/** Schedule runnable to run after everything that's on the queue right now. */
public void post(Runnable runnable) {
synchronized (mQueue) {
mQueue.add(runnable);
if (mQueue.size() == 1) {
scheduleNextLocked();
/** Schedule runnable to run when the queue goes idle. */
public void postIdle(final Runnable runnable) {
post(new IdleRunnable(runnable));
public void cancelRunnable(Runnable runnable) {
synchronized (mQueue) {
while (mQueue.remove(runnable)) { }
public void cancel() {
synchronized (mQueue) {
mQueue.clear();
void scheduleNextLocked() {
if (mQueue.size() & 0) {
// 憋、偷窥
Runnable peek = mQueue.getFirst();
if (peek instanceof IdleRunnable) {
mMessageQueue.addIdleHandler(mHandler);
mHandler.sendEmptyMessage(1);
奉献一个非常好的AsyncTask管理工具:TurAsyncTask.java
* Copyright (C) 2011 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the &License&);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an &AS IS& BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
import java.util.ArrayD
import java.util.ArrayL
import java.util.LinkedL
import java.util.concurrent.ExecutionE
import java.util.concurrent.E
import android.os.AsyncT
* {@link AsyncTask} substitution for the email app.
* Modeled after {@link AsyncTask}; the basic usage is the same, with extra features:
* - Bulk cancellation of multiple tasks.
This is mainly used by UI to cancel pending tasks
in onDestroy() or similar places.
* - Instead of {@link AsyncTask#onPostExecute}, it has {@link #onSuccess(Object)}, as the
regular {@link AsyncTask#onPostExecute} is a bit hard to predict when it'll be called and
whel it won't.
* Note this class is missing some of the {@link AsyncTask} features, e.g. it lacks
* {@link AsyncTask#onProgressUpdate}.
Add these when necessary.
* AsyncTask管理类,可以同时退出,并且可以设置执行是否同时还是顺序执行
* @author Turbo
public abstract class TurAsyncTask&Params, Progress, Result& {
// 连续执行
private static final Executor SERIAL_EXECUTOR = AsyncTask.SERIAL_EXECUTOR;
// 线程池执行,并发执行
private static final Executor PARALLEL_EXECUTOR = AsyncTask.THREAD_POOL_EXECUTOR;
/// M: define a few independent customized serial executors for auto clean attachment task. @{
// 自定义的执行方式,优先执行当前的异步任务,其他的执行结束之后并发执行
public static final Executor SERIAL_EXECUTOR_FOR_AUTO_CLEAR_ATTACH = new TurboSerialExecutor();
//3.0后新增了一个方法executeOnExecutor(Executor exec, Object... params),
//该方法接受2个参数,第一个是Executor,第二个是任务参数。第一个是线程池实例,google为我们预定义了两种:
//第一种是AsyncTask.SERIAL_EXECUTOR,第二种是AsyncTask.THREAD_POOL_EXECUTOR,顾名思义,第一
//种其实就像3.0以后的execute方法,是顺序执行的。第二种就是3.0以前的execute方法,是可以并发执行的。我们直
//接用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);就可以多任务并发执行了。
* M: adding for using independent customized executors for specified email task.
private static class TurboSerialExecutor implements Executor {
final ArrayDeque&Runnable& mTasks = new ArrayDeque&Runnable&();
Runnable mA
// Android中的HashMap被 SparseIntArray代替
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
} finally {
schedul

我要回帖

更多关于 install package 区别 的文章

 

随机推荐