am 68925c53: Merge "Make the loader thread a Looper and move the package manager updates into that thread as well." into gingerbread

Merge commit '68925c535932b7cbb8aafb4f981f23ef2887a1c4'

* commit '68925c535932b7cbb8aafb4f981f23ef2887a1c4':
  Make the loader thread a Looper and move the package manager
This commit is contained in:
Joe Onorato 2010-06-23 10:18:29 -07:00 committed by Android Git Automerger
commit ff68ed0595
2 changed files with 832 additions and 845 deletions

View File

@ -2281,9 +2281,11 @@ public final class Launcher extends Activity
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps) {
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) {
removeDialog(DIALOG_CREATE_SHORTCUT);
if (permanent) {
mWorkspace.removeItems(apps);
}
mAllAppsGrid.removeApps(apps);
}

View File

@ -35,6 +35,8 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@ -60,16 +62,18 @@ import com.android.launcher.R;
*/
public class LauncherModel extends BroadcastReceiver {
static final boolean DEBUG_LOADERS = false;
static final boolean PROFILE_LOADERS = false;
static final String TAG = "Launcher.Model";
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private int mBatchSize; // 0 is all apps at once
private int mAllAppsLoadDelay; // milliseconds between batches
private final LauncherApplication mApp;
private final Object mLock = new Object();
private DeferredHandler mHandler = new DeferredHandler();
private Loader mLoader = new Loader();
private HandlerThread mWorkerThread;
private Handler mWorker;
private LoaderTask mLoaderTask;
// We start off with everything not loaded. After that, we assume that
// our monitoring of the package manager provides all updates and we never
@ -77,12 +81,13 @@ public class LauncherModel extends BroadcastReceiver {
private boolean mWorkspaceLoaded;
private boolean mAllAppsLoaded;
private boolean mBeforeFirstLoad = true; // only access this from main thread
private WeakReference<Callbacks> mCallbacks;
private final Object mAllAppsListLock = new Object();
private AllAppsList mAllAppsList;
private AllAppsList mAllAppsList; // only access in worker thread
private IconCache mIconCache;
final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
private Bitmap mDefaultIcon;
@ -96,7 +101,7 @@ public class LauncherModel extends BroadcastReceiver {
public void bindAllApplications(ArrayList<ApplicationInfo> apps);
public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
public boolean isAllAppsVisible();
}
@ -111,6 +116,10 @@ public class LauncherModel extends BroadcastReceiver {
mAllAppsLoadDelay = app.getResources().getInteger(R.integer.config_allAppsBatchLoadDelay);
mBatchSize = app.getResources().getInteger(R.integer.config_allAppsBatchSize);
mWorkerThread = new HandlerThread("launcher-loader");
mWorkerThread.start();
mWorker = new Handler(mWorkerThread.getLooper());
}
public Bitmap getFallbackIcon() {
@ -284,33 +293,13 @@ public class LauncherModel extends BroadcastReceiver {
}
}
public void startLoader(Context context, boolean isLaunching) {
mLoader.startLoader(context, isLaunching);
}
public void stopLoader() {
mLoader.stopLoader();
}
/**
* Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
* ACTION_PACKAGE_CHANGED.
*/
public void onReceive(Context context, Intent intent) {
// Use the app as the context.
context = mApp;
if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
ArrayList<ApplicationInfo> added = null;
ArrayList<ApplicationInfo> removed = null;
ArrayList<ApplicationInfo> modified = null;
if (mBeforeFirstLoad) {
// If we haven't even loaded yet, don't bother, since we'll just pick
// up the changes.
return;
}
synchronized (mAllAppsListLock) {
final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@ -319,112 +308,43 @@ public class LauncherModel extends BroadcastReceiver {
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
return;
}
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
mAllAppsList.updatePackage(context, packageName);
op = PackageUpdatedTask.OP_UPDATE;
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
if (!replacing) {
mAllAppsList.removePackage(packageName);
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) {
mAllAppsList.addPackage(context, packageName);
op = PackageUpdatedTask.OP_ADD;
} else {
mAllAppsList.updatePackage(context, packageName);
op = PackageUpdatedTask.OP_UPDATE;
}
}
if (mAllAppsList.added.size() > 0) {
added = mAllAppsList.added;
mAllAppsList.added = new ArrayList<ApplicationInfo>();
}
if (mAllAppsList.removed.size() > 0) {
removed = mAllAppsList.removed;
mAllAppsList.removed = new ArrayList<ApplicationInfo>();
for (ApplicationInfo info: removed) {
mIconCache.remove(info.intent.getComponent());
}
}
if (mAllAppsList.modified.size() > 0) {
modified = mAllAppsList.modified;
mAllAppsList.modified = new ArrayList<ApplicationInfo>();
if (op != PackageUpdatedTask.OP_NONE) {
enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
}
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
if (callbacks == null) {
Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
return;
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
if (added != null) {
final ArrayList<ApplicationInfo> addedFinal = added;
mHandler.post(new Runnable() {
public void run() {
callbacks.bindAppsAdded(addedFinal);
}
});
}
if (modified != null) {
final ArrayList<ApplicationInfo> modifiedFinal = modified;
mHandler.post(new Runnable() {
public void run() {
callbacks.bindAppsUpdated(modifiedFinal);
}
});
}
if (removed != null) {
final ArrayList<ApplicationInfo> removedFinal = removed;
mHandler.post(new Runnable() {
public void run() {
callbacks.bindAppsRemoved(removedFinal);
}
});
}
} else {
if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
String packages[] = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (packages == null || packages.length == 0) {
return;
}
synchronized (this) {
mAllAppsLoaded = mWorkspaceLoaded = false;
}
startLoader(context, false);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
String packages[] = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (packages == null || packages.length == 0) {
return;
}
synchronized (this) {
mAllAppsLoaded = mWorkspaceLoaded = false;
}
startLoader(context, false);
}
}
}
}
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
enqueuePackageUpdated(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNAVAILABLE, packages));
public class Loader {
private static final int ITEMS_CHUNK = 6;
private LoaderThread mLoaderThread;
final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
final ArrayList<LauncherAppWidgetInfo> mAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
final HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
/**
* Call this from the ui thread so the handler is initialized on the correct thread.
*/
public Loader() {
}
}
public void startLoader(Context context, boolean isLaunching) {
@ -435,24 +355,25 @@ public class LauncherModel extends BroadcastReceiver {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
LoaderThread oldThread = mLoaderThread;
if (oldThread != null) {
if (oldThread.isLaunching()) {
// If there is already one running, tell it to stop.
LoaderTask oldTask = mLoaderTask;
if (oldTask != null) {
if (oldTask.isLaunching()) {
// don't downgrade isLaunching if we're already running
isLaunching = true;
}
oldThread.stopLocked();
oldTask.stopLocked();
}
mLoaderThread = new LoaderThread(context, oldThread, isLaunching);
mLoaderThread.start();
mLoaderTask = new LoaderTask(context, isLaunching);
mWorker.post(mLoaderTask);
}
}
}
public void stopLoader() {
synchronized (mLock) {
if (mLoaderThread != null) {
mLoaderThread.stopLocked();
if (mLoaderTask != null) {
mLoaderTask.stopLocked();
}
}
}
@ -463,16 +384,15 @@ public class LauncherModel extends BroadcastReceiver {
* - widgets
* - all apps icons
*/
private class LoaderThread extends Thread {
private class LoaderTask implements Runnable {
private Context mContext;
private Thread mWaitThread;
private boolean mIsLaunching;
private boolean mStopped;
private boolean mLoadAndBindStepFinished;
LoaderThread(Context context, Thread waitThread, boolean isLaunching) {
LoaderTask(Context context, boolean isLaunching) {
mContext = context;
mWaitThread = waitThread;
mIsLaunching = isLaunching;
}
@ -480,50 +400,21 @@ public class LauncherModel extends BroadcastReceiver {
return mIsLaunching;
}
/**
* If another LoaderThread was supplied, we need to wait for that to finish before
* we start our processing. This keeps the ordering of the setting and clearing
* of the dirty flags correct by making sure we don't start processing stuff until
* they've had a chance to re-set them. We do this waiting the worker thread, not
* the ui thread to avoid ANRs.
*/
private void waitForOtherThread() {
if (mWaitThread != null) {
boolean done = false;
while (!done) {
try {
mWaitThread.join();
done = true;
} catch (InterruptedException ex) {
// Ignore
}
}
mWaitThread = null;
}
}
private void loadAndBindWorkspace() {
// Load the workspace
// Other other threads can unset mWorkspaceLoaded, so atomically set it,
// and then if they unset it, or we unset it because of mStopped, it will
// be unset.
boolean loaded;
synchronized (this) {
loaded = mWorkspaceLoaded;
mWorkspaceLoaded = true;
}
// For now, just always reload the workspace. It's ~100 ms vs. the
// binding which takes many hundreds of ms.
// We can reconsider.
if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindWorkspace loaded=" + loaded);
if (true || !loaded) {
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
}
if (true || !mWorkspaceLoaded) {
loadWorkspace();
if (mStopped) {
mWorkspaceLoaded = false;
return;
}
mWorkspaceLoaded = true;
}
// Bind the workspace
@ -534,17 +425,17 @@ public class LauncherModel extends BroadcastReceiver {
// 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
// down.
synchronized (LoaderThread.this) {
synchronized (LoaderTask.this) {
final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
mHandler.postIdle(new Runnable() {
public void run() {
synchronized (LoaderThread.this) {
synchronized (LoaderTask.this) {
mLoadAndBindStepFinished = true;
if (DEBUG_LOADERS) {
Log.d(TAG, "done with previous binding step");
}
LoaderThread.this.notify();
LoaderTask.this.notify();
}
}
});
@ -565,14 +456,13 @@ public class LauncherModel extends BroadcastReceiver {
}
public void run() {
waitForOtherThread();
// 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()) : 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) {
@ -580,10 +470,6 @@ public class LauncherModel extends BroadcastReceiver {
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
if (PROFILE_LOADERS) {
android.os.Debug.startMethodTracing("/sdcard/launcher-loaders");
}
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
@ -592,12 +478,18 @@ public class LauncherModel extends BroadcastReceiver {
loadAndBindAllApps();
}
// Whew! Hard work done.
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) {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
waitForIdle();
// second step
if (loadWorkspaceFirst) {
@ -607,32 +499,38 @@ public class LauncherModel extends BroadcastReceiver {
if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
loadAndBindWorkspace();
}
}
// Clear out this reference, otherwise we end up holding it until all of the
// callback runnables are done.
mContext = null;
synchronized (mLock) {
// Setting the reference is atomic, but we can't do it inside the other critical
// sections.
mLoaderThread = null;
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == this) {
mLoaderTask = null;
}
if (PROFILE_LOADERS) {
android.os.Debug.stopMethodTracing();
}
// Trigger a gc to try to clean up after the stuff is done, since the
// renderscript allocations aren't charged to the java heap.
if (mStopped) {
mHandler.post(new Runnable() {
public void run() {
System.gc();
}
});
} else {
mHandler.postIdle(new Runnable() {
public void run() {
System.gc();
}
});
}
}
public void stopLocked() {
synchronized (LoaderThread.this) {
synchronized (LoaderTask.this) {
mStopped = true;
this.notify();
}
@ -989,7 +887,7 @@ public class LauncherModel extends BroadcastReceiver {
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderThread running with no launcher");
Log.w(TAG, "LoaderTask running with no launcher");
return;
}
@ -1089,22 +987,15 @@ public class LauncherModel extends BroadcastReceiver {
}
private void loadAndBindAllApps() {
// Other other threads can unset mAllAppsLoaded, so atomically set it,
// and then if they unset it, or we unset it because of mStopped, it will
// be unset.
boolean loaded;
synchronized (this) {
loaded = mAllAppsLoaded;
mAllAppsLoaded = true;
if (DEBUG_LOADERS) {
Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
}
if (DEBUG_LOADERS) Log.d(TAG, "loadAndBindAllApps loaded=" + loaded);
if (!loaded) {
if (!mAllAppsLoaded) {
loadAllAppsByBatch();
if (mStopped) {
mAllAppsLoaded = false;
return;
}
mAllAppsLoaded = true;
} else {
onlyBindAllApps();
}
@ -1114,7 +1005,7 @@ public class LauncherModel extends BroadcastReceiver {
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderThread running with no launcher (onlyBindAllApps)");
Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
return;
}
@ -1145,7 +1036,7 @@ public class LauncherModel extends BroadcastReceiver {
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
// This launcher has exited and nobody bothered to tell us. Just bail.
Log.w(TAG, "LoaderThread running with no launcher (loadAllAppsByBatch)");
Log.w(TAG, "LoaderTask running with no launcher (loadAllAppsByBatch)");
return;
}
@ -1161,12 +1052,7 @@ public class LauncherModel extends BroadcastReceiver {
int i=0;
int batchSize = -1;
while (i < N && !mStopped) {
synchronized (mAllAppsListLock) {
if (i == 0) {
// This needs to happen inside the same lock block as when we
// prepare the first batch for bindAllApplications. Otherwise
// the package changed receiver can come in and double-add
// (or miss one?).
mAllAppsList.clear();
final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
apps = packageManager.queryIntentActivities(mainIntent, 0);
@ -1219,7 +1105,6 @@ public class LauncherModel extends BroadcastReceiver {
final long t = SystemClock.uptimeMillis();
if (callbacks != null) {
if (first) {
mBeforeFirstLoad = false;
callbacks.bindAllApplications(added);
} else {
callbacks.bindAppsAdded(added);
@ -1238,7 +1123,6 @@ public class LauncherModel extends BroadcastReceiver {
Log.d(TAG, "batch of " + (i-startIndex) + " icons processed in "
+ (SystemClock.uptimeMillis()-t2) + "ms");
}
}
if (mAllAppsLoadDelay > 0 && i < N) {
try {
@ -1258,20 +1142,117 @@ public class LauncherModel extends BroadcastReceiver {
}
public void dumpState() {
Log.d(TAG, "mLoader.mLoaderThread.mContext=" + mContext);
Log.d(TAG, "mLoader.mLoaderThread.mWaitThread=" + mWaitThread);
Log.d(TAG, "mLoader.mLoaderThread.mIsLaunching=" + mIsLaunching);
Log.d(TAG, "mLoader.mLoaderThread.mStopped=" + mStopped);
Log.d(TAG, "mLoader.mLoaderThread.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
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);
}
}
public void dumpState() {
Log.d(TAG, "mLoader.mItems size=" + mLoader.mItems.size());
if (mLoaderThread != null) {
mLoaderThread.dumpState();
} else {
Log.d(TAG, "mLoader.mLoaderThread=null");
void enqueuePackageUpdated(PackageUpdatedTask task) {
mWorker.post(task);
}
private class PackageUpdatedTask implements Runnable {
int mOp;
String[] mPackages;
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) {
mOp = op;
mPackages = packages;
}
public void run() {
final Context context = mApp;
final String[] packages = mPackages;
final int N = packages.length;
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]);
}
break;
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]);
}
break;
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]);
}
break;
}
ArrayList<ApplicationInfo> added = null;
ArrayList<ApplicationInfo> removed = null;
ArrayList<ApplicationInfo> modified = null;
if (mAllAppsList.added.size() > 0) {
added = mAllAppsList.added;
mAllAppsList.added = new ArrayList<ApplicationInfo>();
}
if (mAllAppsList.removed.size() > 0) {
removed = mAllAppsList.removed;
mAllAppsList.removed = new ArrayList<ApplicationInfo>();
for (ApplicationInfo info: removed) {
mIconCache.remove(info.intent.getComponent());
}
}
if (mAllAppsList.modified.size() > 0) {
modified = mAllAppsList.modified;
mAllAppsList.modified = new ArrayList<ApplicationInfo>();
}
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
if (callbacks == null) {
Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
return;
}
if (added != null) {
final ArrayList<ApplicationInfo> addedFinal = added;
mHandler.post(new Runnable() {
public void run() {
if (callbacks == mCallbacks.get()) {
callbacks.bindAppsAdded(addedFinal);
}
}
});
}
if (modified != null) {
final ArrayList<ApplicationInfo> modifiedFinal = modified;
mHandler.post(new Runnable() {
public void run() {
if (callbacks == mCallbacks.get()) {
callbacks.bindAppsUpdated(modifiedFinal);
}
}
});
}
if (removed != null) {
final boolean permanent = mOp != OP_UNAVAILABLE;
final ArrayList<ApplicationInfo> removedFinal = removed;
mHandler.post(new Runnable() {
public void run() {
if (callbacks == mCallbacks.get()) {
callbacks.bindAppsRemoved(removedFinal, permanent);
}
}
});
}
}
}
@ -1583,12 +1564,16 @@ public class LauncherModel extends BroadcastReceiver {
};
public void dumpState() {
Log.d(TAG, "mBeforeFirstLoad=" + mBeforeFirstLoad);
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);
mLoader.dumpState();
Log.d(TAG, "mItems size=" + mItems.size());
if (mLoaderTask != null) {
mLoaderTask.dumpState();
} else {
Log.d(TAG, "mLoaderTask=null");
}
}
}