diff options
Diffstat (limited to 'core/java/android/app/LoaderManager.java')
-rw-r--r-- | core/java/android/app/LoaderManager.java | 272 |
1 files changed, 199 insertions, 73 deletions
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index a0d2aec..31e3c40 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -26,6 +26,12 @@ import android.util.SparseArray; * one or more {@link android.content.Loader} instances associated with it. */ public class LoaderManager { + final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(); + final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); + boolean mStarted; + boolean mRetaining; + boolean mRetainingStarted; + /** * Callback interface for a client to interact with the manager. */ @@ -35,81 +41,208 @@ public class LoaderManager { } final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> { - public Bundle args; - public Loader<Object> loader; - public LoaderManager.LoaderCallbacks<Object> callback; + final int mId; + final Bundle mArgs; + LoaderManager.LoaderCallbacks<Object> mCallbacks; + Loader<Object> mLoader; + Object mData; + boolean mStarted; + boolean mRetaining; + boolean mRetainingStarted; + boolean mDestroyed; + boolean mListenerRegistered; + + public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) { + mId = id; + mArgs = args; + mCallbacks = callbacks; + } + + void start() { + if (mRetaining && mRetainingStarted) { + // Our owner is started, but we were being retained from a + // previous instance in the started state... so there is really + // nothing to do here, since the loaders are still started. + mStarted = true; + return; + } + + if (mLoader == null && mCallbacks != null) { + mLoader = mCallbacks.onCreateLoader(mId, mArgs); + } + if (mLoader != null) { + mLoader.registerListener(mId, this); + mListenerRegistered = true; + mLoader.startLoading(); + mStarted = true; + } + } + + void retain() { + mRetaining = true; + mRetainingStarted = mStarted; + mStarted = false; + mCallbacks = null; + } + + void finishRetain() { + if (mRetaining) { + mRetaining = false; + if (mStarted != mRetainingStarted) { + if (!mStarted) { + // This loader was retained in a started state, but + // at the end of retaining everything our owner is + // no longer started... so make it stop. + stop(); + } + } + if (mStarted && mData != null && mCallbacks != null) { + // This loader was retained, and now at the point of + // finishing the retain we find we remain started, have + // our data, and the owner has a new callback... so + // let's deliver the data now. + mCallbacks.onLoadFinished(mLoader, mData); + } + } + } + + void stop() { + mStarted = false; + if (mLoader != null && mListenerRegistered) { + // Let the loader know we're done with it + mListenerRegistered = false; + mLoader.unregisterListener(this); + } + } + + void destroy() { + mDestroyed = true; + mCallbacks = null; + if (mLoader != null) { + if (mListenerRegistered) { + mListenerRegistered = false; + mLoader.unregisterListener(this); + } + mLoader.destroy(); + } + } @Override public void onLoadComplete(Loader<Object> loader, Object data) { + if (mDestroyed) { + return; + } + // Notify of the new data so the app can switch out the old data before // we try to destroy it. - callback.onLoadFinished(loader, data); + mData = data; + if (mCallbacks != null) { + mCallbacks.onLoadFinished(loader, data); + } // Look for an inactive loader and destroy it if found - int id = loader.getId(); - LoaderInfo info = mInactiveLoaders.get(id); + LoaderInfo info = mInactiveLoaders.get(mId); if (info != null) { - Loader<Object> oldLoader = info.loader; + Loader<Object> oldLoader = info.mLoader; if (oldLoader != null) { + oldLoader.unregisterListener(info); oldLoader.destroy(); } - mInactiveLoaders.remove(id); + mInactiveLoaders.remove(mId); } } } - - SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(); - SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); - boolean mStarted; LoaderManager(boolean started) { mStarted = started; } + private LoaderInfo createLoader(int id, Bundle args, + LoaderManager.LoaderCallbacks<Object> callback) { + LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); + mLoaders.put(id, info); + Loader<Object> loader = callback.onCreateLoader(id, args); + info.mLoader = (Loader<Object>)loader; + if (mStarted) { + // The activity will start all existing loaders in it's onStart(), so only start them + // here if we're past that point of the activitiy's life cycle + loader.registerListener(id, info); + loader.startLoading(); + } + return info; + } + + /** + * Ensures a loader is initialized an active. If the loader doesn't + * already exist, one is created and started. Otherwise the last created + * loader is re-used. + * + * <p>In either case, the given callback is associated with the loader, and + * will be called as the loader state changes. If at the point of call + * the caller is in its started state, and the requested loader + * already exists and has generated its data, then + * callback.{@link LoaderCallbacks#onLoadFinished(Loader, Object)} will + * be called immediately (inside of this function), so you must be prepared + * for this to happen. + */ + @SuppressWarnings("unchecked") + public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { + LoaderInfo info = mLoaders.get(id); + + if (info == null) { + // Loader doesn't already exist; create. + info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); + } else { + info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback; + } + + if (info.mData != null && mStarted) { + // If the loader has already generated its data, report it now. + info.mCallbacks.onLoadFinished(info.mLoader, info.mData); + } + + return (Loader<D>)info.mLoader; + } + /** - * Associates a loader with this managers, registers the callbacks on it, + * Create a new loader in this manager, registers the callbacks to it, * and starts it loading. If a loader with the same id has previously been * started it will automatically be destroyed when the new loader completes * its work. The callback will be delivered before the old loader * is destroyed. */ @SuppressWarnings("unchecked") - public <D> Loader<D> startLoading(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { + public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { LoaderInfo info = mLoaders.get(id); if (info != null) { - // Keep track of the previous instance of this loader so we can destroy - // it when the new one completes. - mInactiveLoaders.put(id, info); - } - info = new LoaderInfo(); - info.args = args; - info.callback = (LoaderManager.LoaderCallbacks<Object>)callback; - mLoaders.put(id, info); - Loader<D> loader = callback.onCreateLoader(id, args); - info.loader = (Loader<Object>)loader; - if (mStarted) { - // The activity will start all existing loaders in it's onStart(), so only start them - // here if we're past that point of the activitiy's life cycle - loader.registerListener(id, (OnLoadCompleteListener<D>)info); - loader.startLoading(); + if (mInactiveLoaders.get(id) != null) { + // We already have an inactive loader for this ID that we are + // waiting for! Now we have three active loaders... let's just + // drop the one in the middle, since we are still waiting for + // its result but that result is already out of date. + info.destroy(); + } else { + // Keep track of the previous instance of this loader so we can destroy + // it when the new one completes. + mInactiveLoaders.put(id, info); + } } - return loader; + info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); + return (Loader<D>)info.mLoader; } /** * Stops and removes the loader with the given ID. */ - public void stopLoading(int id) { - if (mLoaders != null) { - int idx = mLoaders.indexOfKey(id); - if (idx >= 0) { - LoaderInfo info = mLoaders.valueAt(idx); - mLoaders.removeAt(idx); - Loader<Object> loader = info.loader; - if (loader != null) { - loader.unregisterListener(info); - loader.destroy(); - } + public void stopLoader(int id) { + int idx = mLoaders.indexOfKey(id); + if (idx >= 0) { + LoaderInfo info = mLoaders.valueAt(idx); + mLoaders.removeAt(idx); + Loader<Object> loader = info.mLoader; + if (loader != null) { + loader.unregisterListener(info); + loader.destroy(); } } } @@ -122,7 +255,7 @@ public class LoaderManager { public <D> Loader<D> getLoader(int id) { LoaderInfo loaderInfo = mLoaders.get(id); if (loaderInfo != null) { - return (Loader<D>)mLoaders.get(id).loader; + return (Loader<D>)mLoaders.get(id).mLoader; } return null; } @@ -131,50 +264,43 @@ public class LoaderManager { // Call out to sub classes so they can start their loaders // Let the existing loaders know that we want to be notified when a load is complete for (int i = mLoaders.size()-1; i >= 0; i--) { - LoaderInfo info = mLoaders.valueAt(i); - Loader<Object> loader = info.loader; - int id = mLoaders.keyAt(i); - if (loader == null) { - loader = info.callback.onCreateLoader(id, info.args); - info.loader = loader; - } - loader.registerListener(id, info); - loader.startLoading(); + mLoaders.valueAt(i).start(); } - mStarted = true; } void doStop() { for (int i = mLoaders.size()-1; i >= 0; i--) { - LoaderInfo info = mLoaders.valueAt(i); - Loader<Object> loader = info.loader; - if (loader == null) { - continue; - } - - // Let the loader know we're done with it - loader.unregisterListener(info); - - // The loader isn't getting passed along to the next instance so ask it to stop loading - //if (!getActivity().isChangingConfigurations()) { - // loader.stopLoading(); - //} + mLoaders.valueAt(i).stop(); } - mStarted = false; } + void doRetain() { + mRetaining = true; + mStarted = false; + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).retain(); + } + } + + void finishRetain() { + mRetaining = false; + for (int i = mLoaders.size()-1; i >= 0; i--) { + mLoaders.valueAt(i).finishRetain(); + } + } + void doDestroy() { - if (mLoaders != null) { + if (!mRetaining) { for (int i = mLoaders.size()-1; i >= 0; i--) { - LoaderInfo info = mLoaders.valueAt(i); - Loader<Object> loader = info.loader; - if (loader == null) { - continue; - } - loader.destroy(); + mLoaders.valueAt(i).destroy(); } } + + for (int i = mInactiveLoaders.size()-1; i >= 0; i--) { + mInactiveLoaders.valueAt(i).destroy(); + } + mInactiveLoaders.clear(); } } |