Loading api/current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -4670,10 +4670,9 @@ package android.content { public abstract class AsyncTaskLoader extends android.content.Loader { ctor public AsyncTaskLoader(android.content.Context); method public boolean cancelLoad(); method protected boolean isLoadInBackgroundCanceled(); method public void cancelLoadInBackground(); method public boolean isLoadInBackgroundCanceled(); method public abstract D loadInBackground(); method protected void onCancelLoadInBackground(); method public void onCanceled(D); method protected D onLoadInBackground(); method public void setUpdateThrottle(long); Loading Loading @@ -5765,7 +5764,9 @@ package android.content { public class Loader { ctor public Loader(android.content.Context); method public void abandon(); method public boolean cancelLoad(); method public java.lang.String dataToString(D); method public void deliverCancellation(); method public void deliverResult(D); method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void forceLoad(); Loading @@ -5775,23 +5776,30 @@ package android.content { method public boolean isReset(); method public boolean isStarted(); method protected void onAbandon(); method protected boolean onCancelLoad(); method public void onContentChanged(); method protected void onForceLoad(); method protected void onReset(); method protected void onStartLoading(); method protected void onStopLoading(); method public void registerListener(int, android.content.Loader.OnLoadCompleteListener<D>); method public void registerOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>); method public void reset(); method public final void startLoading(); method public void stopLoading(); method public boolean takeContentChanged(); method public void unregisterListener(android.content.Loader.OnLoadCompleteListener<D>); method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>); } public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver { ctor public Loader.ForceLoadContentObserver(); } public static abstract interface Loader.OnLoadCanceledListener { method public abstract void onLoadCanceled(android.content.Loader<D>); } public static abstract interface Loader.OnLoadCompleteListener { method public abstract void onLoadComplete(android.content.Loader<D>, D); } core/java/android/app/LoaderManager.java +51 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app; import android.content.Loader; import android.content.Loader.OnLoadCanceledListener; import android.os.Bundle; import android.util.DebugUtils; import android.util.Log; Loading Loading @@ -219,7 +220,8 @@ class LoaderManagerImpl extends LoaderManager { boolean mCreatingLoader; final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> { final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>, Loader.OnLoadCanceledListener<Object> { final int mId; final Bundle mArgs; LoaderManager.LoaderCallbacks<Object> mCallbacks; Loading Loading @@ -271,6 +273,7 @@ class LoaderManagerImpl extends LoaderManager { } if (!mListenerRegistered) { mLoader.registerListener(mId, this); mLoader.registerOnLoadCanceledListener(this); mListenerRegistered = true; } mLoader.startLoading(); Loading Loading @@ -329,11 +332,21 @@ class LoaderManagerImpl extends LoaderManager { // Let the loader know we're done with it mListenerRegistered = false; mLoader.unregisterListener(this); mLoader.unregisterOnLoadCanceledListener(this); mLoader.stopLoading(); } } } void cancel() { if (DEBUG) Log.v(TAG, " Canceling: " + this); if (mStarted && mLoader != null && mListenerRegistered) { if (!mLoader.cancelLoad()) { onLoadCanceled(mLoader); } } } void destroy() { if (DEBUG) Log.v(TAG, " Destroying: " + this); mDestroyed = true; Loading Loading @@ -361,6 +374,7 @@ class LoaderManagerImpl extends LoaderManager { if (mListenerRegistered) { mListenerRegistered = false; mLoader.unregisterListener(this); mLoader.unregisterOnLoadCanceledListener(this); } mLoader.reset(); } Loading @@ -369,7 +383,37 @@ class LoaderManagerImpl extends LoaderManager { } } @Override public void onLoadComplete(Loader<Object> loader, Object data) { @Override public void onLoadCanceled(Loader<Object> loader) { if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this); if (mDestroyed) { if (DEBUG) Log.v(TAG, " Ignoring load canceled -- destroyed"); return; } if (mLoaders.get(mId) != this) { // This cancellation message is not coming from the current active loader. // We don't care about it. if (DEBUG) Log.v(TAG, " Ignoring load canceled -- not active"); return; } LoaderInfo pending = mPendingLoader; if (pending != null) { // There is a new request pending and we were just // waiting for the old one to cancel or complete before starting // it. So now it is time, switch over to the new loader. if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending); mPendingLoader = null; mLoaders.put(mId, null); destroy(); installLoader(pending); } } @Override public void onLoadComplete(Loader<Object> loader, Object data) { if (DEBUG) Log.v(TAG, "onLoadComplete: " + this); if (mDestroyed) { Loading Loading @@ -632,7 +676,9 @@ class LoaderManagerImpl extends LoaderManager { } else { // Now we have three active loaders... we'll queue // up this request to be processed once one of the other loaders // finishes. // finishes or is canceled. if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel"); info.cancel(); if (info.mPendingLoader != null) { if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader); info.mPendingLoader.destroy(); Loading core/java/android/content/AsyncTaskLoader.java +96 −50 Original line number Diff line number Diff line Loading @@ -53,19 +53,33 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { static final boolean DEBUG = false; final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { private final CountDownLatch mDone = new CountDownLatch(1); D result; // Set to true to indicate that the task has been posted to a handler for // execution at a later time. Used to throttle updates. boolean waiting; private CountDownLatch done = new CountDownLatch(1); /* Runs on a worker thread */ @Override protected D doInBackground(Void... params) { if (DEBUG) Slog.v(TAG, this + " >>> doInBackground"); result = AsyncTaskLoader.this.onLoadInBackground(); try { D data = AsyncTaskLoader.this.onLoadInBackground(); if (DEBUG) Slog.v(TAG, this + " <<< doInBackground"); return result; return data; } catch (OperationCanceledException ex) { if (!isCancelled()) { // onLoadInBackground threw a canceled exception spuriously. // This is problematic because it means that the LoaderManager did not // cancel the Loader itself and still expects to receive a result. // Additionally, the Loader's own state will not have been updated to // reflect the fact that the task was being canceled. // So we treat this case as an unhandled exception. throw ex; } if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)"); return null; } } /* Runs on the UI thread */ Loading @@ -75,25 +89,37 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { try { AsyncTaskLoader.this.dispatchOnLoadComplete(this, data); } finally { done.countDown(); mDone.countDown(); } } /* Runs on the UI thread */ @Override protected void onCancelled() { protected void onCancelled(D data) { if (DEBUG) Slog.v(TAG, this + " onCancelled"); try { AsyncTaskLoader.this.dispatchOnCancelled(this, result); AsyncTaskLoader.this.dispatchOnCancelled(this, data); } finally { done.countDown(); mDone.countDown(); } } /* Runs on the UI thread, when the waiting task is posted to a handler. * This method is only executed when task execution was deferred (waiting was true). */ @Override public void run() { waiting = false; AsyncTaskLoader.this.executePendingTask(); } /* Used for testing purposes to wait for the task to complete. */ public void waitForLoader() { try { mDone.await(); } catch (InterruptedException e) { // Ignore } } } volatile LoadTask mTask; Loading @@ -109,7 +135,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Set amount to throttle updates by. This is the minimum time from * when the last {@link #onLoadInBackground()} call has completed until * when the last {@link #loadInBackground()} call has completed until * a new load is scheduled. * * @param delayMS Amount of delay, in milliseconds. Loading @@ -130,24 +156,9 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { executePendingTask(); } /** * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)} * for more info. Must be called on the main thread of the process. * * <p>Cancelling is not an immediate operation, since the load is performed * in a background thread. If there is currently a load in progress, this * method requests that the load be cancelled, and notes this is the case; * once the background thread has completed its work its remaining state * will be cleared. If another load request comes in during this time, * it will be held until the cancelled load is complete. * * @return Returns <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. */ public boolean cancelLoad() { if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask); @Override protected boolean onCancelLoad() { if (DEBUG) Slog.v(TAG, "onCancelLoad: mTask=" + mTask); if (mTask != null) { if (mCancellingTask != null) { // There was a pending task already waiting for a previous Loading @@ -173,7 +184,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled); if (cancelled) { mCancellingTask = mTask; onCancelLoadInBackground(); cancelLoadInBackground(); } mTask = null; return cancelled; Loading @@ -184,7 +195,10 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Called if the task was canceled before it was completed. Gives the class a chance * to properly dispose of the result. * to clean up post-cancellation and to properly dispose of the result. * * @param data The value that was returned by {@link #loadInBackground}, or null * if the task threw {@link OperationCanceledException}. */ public void onCanceled(D data) { } Loading Loading @@ -218,6 +232,8 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!"); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mCancellingTask = null; if (DEBUG) Slog.v(TAG, "Delivering cancellation"); deliverCancellation(); executePendingTask(); } } Loading @@ -240,38 +256,72 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { } /** * Called on a worker thread to perform the actual load and to return * the result of the load operation. * * Implementations should not deliver the result directly, but should return them * from this method, which will eventually end up calling {@link #deliverResult} on * the UI thread. If implementations need to process the results on the UI thread * they may override {@link #deliverResult} and do so there. * * To support cancellation, this method should periodically check the value of * {@link #isLoadInBackgroundCanceled} and terminate when it returns true. * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load * directly instead of polling {@link #isLoadInBackgroundCanceled}. * * When the load is canceled, this method may either return normally or throw * {@link OperationCanceledException}. In either case, the {@link Loader} will * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the * result object, if any. * * @return The result of the load operation. * * @throws OperationCanceledException if the load is canceled during execution. * * @see #isLoadInBackgroundCanceled * @see #cancelLoadInBackground * @see #onCanceled */ public abstract D loadInBackground(); /** * Called on a worker thread to perform the actual load. Implementations should not deliver the * result directly, but should return them from this method, which will eventually end up * calling {@link #deliverResult} on the UI thread. If implementations need to process * the results on the UI thread they may override {@link #deliverResult} and do so * there. * Calls {@link #loadInBackground()}. * * This method is reserved for use by the loader framework. * Subclasses should override {@link #loadInBackground} instead of this method. * * @return The result of the load operation. * * @return Implementations must return the result of their load operation. * @throws OperationCanceledException if the load is canceled during execution. * * @see #loadInBackground */ protected D onLoadInBackground() { return loadInBackground(); } /** * Override this method to try to abort the computation currently taking * place on a background thread. * Called on the main thread to abort a load in progress. * * Override this method to abort the current invocation of {@link #loadInBackground} * that is running in the background on a worker thread. * * Note that when this method is called, it is possible that {@link #loadInBackground} * has not started yet or has already completed. * This method should do nothing if {@link #loadInBackground} has not started * running or if it has already finished. * * @see #loadInBackground */ protected void onCancelLoadInBackground() { public void cancelLoadInBackground() { } /** * Returns true if the current execution of {@link #loadInBackground()} is being canceled. * Returns true if the current invocation of {@link #loadInBackground} is being canceled. * * @return True if the current invocation of {@link #loadInBackground} is being canceled. * * @return True if the current execution of {@link #loadInBackground()} is being canceled. * @see #loadInBackground */ protected boolean isLoadInBackgroundCanceled() { public boolean isLoadInBackgroundCanceled() { return mCancellingTask != null; } Loading @@ -288,11 +338,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { public void waitForLoader() { LoadTask task = mTask; if (task != null) { try { task.done.await(); } catch (InterruptedException e) { // Ignore } task.waitForLoader(); } } Loading core/java/android/content/CursorLoader.java +2 −2 Original line number Diff line number Diff line Loading @@ -76,8 +76,8 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { } @Override protected void onCancelLoadInBackground() { super.onCancelLoadInBackground(); public void cancelLoadInBackground() { super.cancelLoadInBackground(); synchronized (this) { if (mCancelationSignal != null) { Loading core/java/android/content/Loader.java +101 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import java.io.PrintWriter; public class Loader<D> { int mId; OnLoadCompleteListener<D> mListener; OnLoadCanceledListener<D> mOnLoadCanceledListener; Context mContext; boolean mStarted = false; boolean mAbandoned = false; Loading Loading @@ -99,6 +100,23 @@ public class Loader<D> { public void onLoadComplete(Loader<D> loader, D data); } /** * Interface that is implemented to discover when a Loader has been canceled * before it finished loading its data. You do not normally need to implement * this yourself; it is used in the implementation of {@link android.app.LoaderManager} * to find out when a Loader it is managing has been canceled so that it * can schedule the next Loader. This interface should only be used if a * Loader is not being used in conjunction with LoaderManager. */ public interface OnLoadCanceledListener<D> { /** * Called on the thread that created the Loader when the load is canceled. * * @param loader the loader that canceled the load */ public void onLoadCanceled(Loader<D> loader); } /** * Stores away the application context associated with context. * Since Loaders can be used across multiple activities it's dangerous to Loading Loading @@ -126,6 +144,18 @@ public class Loader<D> { } } /** * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled. * Should only be called by subclasses. * * Must be called from the process's main thread. */ public void deliverCancellation() { if (mOnLoadCanceledListener != null) { mOnLoadCanceledListener.onLoadCanceled(this); } } /** * @return an application context retrieved from the Context passed to the constructor. */ Loading Loading @@ -170,6 +200,40 @@ public class Loader<D> { mListener = null; } /** * Registers a listener that will receive callbacks when a load is canceled. * The callback will be called on the process's main thread so it's safe to * pass the results to widgets. * * Must be called from the process's main thread. * * @param listener The listener to register. */ public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { if (mOnLoadCanceledListener != null) { throw new IllegalStateException("There is already a listener registered"); } mOnLoadCanceledListener = listener; } /** * Unregisters a listener that was previously added with * {@link #registerOnLoadCanceledListener}. * * Must be called from the process's main thread. * * @param listener The listener to unregister. */ public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { if (mOnLoadCanceledListener == null) { throw new IllegalStateException("No listener register"); } if (mOnLoadCanceledListener != listener) { throw new IllegalArgumentException("Attempting to unregister the wrong listener"); } mOnLoadCanceledListener = null; } /** * Return whether this load has been started. That is, its {@link #startLoading()} * has been called and no calls to {@link #stopLoading()} or Loading Loading @@ -233,6 +297,43 @@ public class Loader<D> { protected void onStartLoading() { } /** * Attempt to cancel the current load task. * Must be called on the main thread of the process. * * <p>Cancellation is not an immediate operation, since the load is performed * in a background thread. If there is currently a load in progress, this * method requests that the load be canceled, and notes this is the case; * once the background thread has completed its work its remaining state * will be cleared. If another load request comes in during this time, * it will be held until the canceled load is complete. * * @return Returns <tt>false</tt> if the task could not be canceled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task * is still running and the {@link OnLoadCanceledListener} will be called * when the task completes. */ public boolean cancelLoad() { return onCancelLoad(); } /** * Subclasses must implement this to take care of requests to {@link #cancelLoad()}. * This will always be called from the process's main thread. * * @return Returns <tt>false</tt> if the task could not be canceled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task * is still running and the {@link OnLoadCanceledListener} will be called * when the task completes. */ protected boolean onCancelLoad() { return false; } /** * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously * loaded data set and load a new one. This simply calls through to the Loading Loading
api/current.txt +11 −3 Original line number Diff line number Diff line Loading @@ -4670,10 +4670,9 @@ package android.content { public abstract class AsyncTaskLoader extends android.content.Loader { ctor public AsyncTaskLoader(android.content.Context); method public boolean cancelLoad(); method protected boolean isLoadInBackgroundCanceled(); method public void cancelLoadInBackground(); method public boolean isLoadInBackgroundCanceled(); method public abstract D loadInBackground(); method protected void onCancelLoadInBackground(); method public void onCanceled(D); method protected D onLoadInBackground(); method public void setUpdateThrottle(long); Loading Loading @@ -5765,7 +5764,9 @@ package android.content { public class Loader { ctor public Loader(android.content.Context); method public void abandon(); method public boolean cancelLoad(); method public java.lang.String dataToString(D); method public void deliverCancellation(); method public void deliverResult(D); method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public void forceLoad(); Loading @@ -5775,23 +5776,30 @@ package android.content { method public boolean isReset(); method public boolean isStarted(); method protected void onAbandon(); method protected boolean onCancelLoad(); method public void onContentChanged(); method protected void onForceLoad(); method protected void onReset(); method protected void onStartLoading(); method protected void onStopLoading(); method public void registerListener(int, android.content.Loader.OnLoadCompleteListener<D>); method public void registerOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>); method public void reset(); method public final void startLoading(); method public void stopLoading(); method public boolean takeContentChanged(); method public void unregisterListener(android.content.Loader.OnLoadCompleteListener<D>); method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>); } public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver { ctor public Loader.ForceLoadContentObserver(); } public static abstract interface Loader.OnLoadCanceledListener { method public abstract void onLoadCanceled(android.content.Loader<D>); } public static abstract interface Loader.OnLoadCompleteListener { method public abstract void onLoadComplete(android.content.Loader<D>, D); }
core/java/android/app/LoaderManager.java +51 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.app; import android.content.Loader; import android.content.Loader.OnLoadCanceledListener; import android.os.Bundle; import android.util.DebugUtils; import android.util.Log; Loading Loading @@ -219,7 +220,8 @@ class LoaderManagerImpl extends LoaderManager { boolean mCreatingLoader; final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> { final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>, Loader.OnLoadCanceledListener<Object> { final int mId; final Bundle mArgs; LoaderManager.LoaderCallbacks<Object> mCallbacks; Loading Loading @@ -271,6 +273,7 @@ class LoaderManagerImpl extends LoaderManager { } if (!mListenerRegistered) { mLoader.registerListener(mId, this); mLoader.registerOnLoadCanceledListener(this); mListenerRegistered = true; } mLoader.startLoading(); Loading Loading @@ -329,11 +332,21 @@ class LoaderManagerImpl extends LoaderManager { // Let the loader know we're done with it mListenerRegistered = false; mLoader.unregisterListener(this); mLoader.unregisterOnLoadCanceledListener(this); mLoader.stopLoading(); } } } void cancel() { if (DEBUG) Log.v(TAG, " Canceling: " + this); if (mStarted && mLoader != null && mListenerRegistered) { if (!mLoader.cancelLoad()) { onLoadCanceled(mLoader); } } } void destroy() { if (DEBUG) Log.v(TAG, " Destroying: " + this); mDestroyed = true; Loading Loading @@ -361,6 +374,7 @@ class LoaderManagerImpl extends LoaderManager { if (mListenerRegistered) { mListenerRegistered = false; mLoader.unregisterListener(this); mLoader.unregisterOnLoadCanceledListener(this); } mLoader.reset(); } Loading @@ -369,7 +383,37 @@ class LoaderManagerImpl extends LoaderManager { } } @Override public void onLoadComplete(Loader<Object> loader, Object data) { @Override public void onLoadCanceled(Loader<Object> loader) { if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this); if (mDestroyed) { if (DEBUG) Log.v(TAG, " Ignoring load canceled -- destroyed"); return; } if (mLoaders.get(mId) != this) { // This cancellation message is not coming from the current active loader. // We don't care about it. if (DEBUG) Log.v(TAG, " Ignoring load canceled -- not active"); return; } LoaderInfo pending = mPendingLoader; if (pending != null) { // There is a new request pending and we were just // waiting for the old one to cancel or complete before starting // it. So now it is time, switch over to the new loader. if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending); mPendingLoader = null; mLoaders.put(mId, null); destroy(); installLoader(pending); } } @Override public void onLoadComplete(Loader<Object> loader, Object data) { if (DEBUG) Log.v(TAG, "onLoadComplete: " + this); if (mDestroyed) { Loading Loading @@ -632,7 +676,9 @@ class LoaderManagerImpl extends LoaderManager { } else { // Now we have three active loaders... we'll queue // up this request to be processed once one of the other loaders // finishes. // finishes or is canceled. if (DEBUG) Log.v(TAG, " Current loader is running; attempting to cancel"); info.cancel(); if (info.mPendingLoader != null) { if (DEBUG) Log.v(TAG, " Removing pending loader: " + info.mPendingLoader); info.mPendingLoader.destroy(); Loading
core/java/android/content/AsyncTaskLoader.java +96 −50 Original line number Diff line number Diff line Loading @@ -53,19 +53,33 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { static final boolean DEBUG = false; final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { private final CountDownLatch mDone = new CountDownLatch(1); D result; // Set to true to indicate that the task has been posted to a handler for // execution at a later time. Used to throttle updates. boolean waiting; private CountDownLatch done = new CountDownLatch(1); /* Runs on a worker thread */ @Override protected D doInBackground(Void... params) { if (DEBUG) Slog.v(TAG, this + " >>> doInBackground"); result = AsyncTaskLoader.this.onLoadInBackground(); try { D data = AsyncTaskLoader.this.onLoadInBackground(); if (DEBUG) Slog.v(TAG, this + " <<< doInBackground"); return result; return data; } catch (OperationCanceledException ex) { if (!isCancelled()) { // onLoadInBackground threw a canceled exception spuriously. // This is problematic because it means that the LoaderManager did not // cancel the Loader itself and still expects to receive a result. // Additionally, the Loader's own state will not have been updated to // reflect the fact that the task was being canceled. // So we treat this case as an unhandled exception. throw ex; } if (DEBUG) Slog.v(TAG, this + " <<< doInBackground (was canceled)"); return null; } } /* Runs on the UI thread */ Loading @@ -75,25 +89,37 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { try { AsyncTaskLoader.this.dispatchOnLoadComplete(this, data); } finally { done.countDown(); mDone.countDown(); } } /* Runs on the UI thread */ @Override protected void onCancelled() { protected void onCancelled(D data) { if (DEBUG) Slog.v(TAG, this + " onCancelled"); try { AsyncTaskLoader.this.dispatchOnCancelled(this, result); AsyncTaskLoader.this.dispatchOnCancelled(this, data); } finally { done.countDown(); mDone.countDown(); } } /* Runs on the UI thread, when the waiting task is posted to a handler. * This method is only executed when task execution was deferred (waiting was true). */ @Override public void run() { waiting = false; AsyncTaskLoader.this.executePendingTask(); } /* Used for testing purposes to wait for the task to complete. */ public void waitForLoader() { try { mDone.await(); } catch (InterruptedException e) { // Ignore } } } volatile LoadTask mTask; Loading @@ -109,7 +135,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Set amount to throttle updates by. This is the minimum time from * when the last {@link #onLoadInBackground()} call has completed until * when the last {@link #loadInBackground()} call has completed until * a new load is scheduled. * * @param delayMS Amount of delay, in milliseconds. Loading @@ -130,24 +156,9 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { executePendingTask(); } /** * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)} * for more info. Must be called on the main thread of the process. * * <p>Cancelling is not an immediate operation, since the load is performed * in a background thread. If there is currently a load in progress, this * method requests that the load be cancelled, and notes this is the case; * once the background thread has completed its work its remaining state * will be cleared. If another load request comes in during this time, * it will be held until the cancelled load is complete. * * @return Returns <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. */ public boolean cancelLoad() { if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask); @Override protected boolean onCancelLoad() { if (DEBUG) Slog.v(TAG, "onCancelLoad: mTask=" + mTask); if (mTask != null) { if (mCancellingTask != null) { // There was a pending task already waiting for a previous Loading @@ -173,7 +184,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled); if (cancelled) { mCancellingTask = mTask; onCancelLoadInBackground(); cancelLoadInBackground(); } mTask = null; return cancelled; Loading @@ -184,7 +195,10 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { /** * Called if the task was canceled before it was completed. Gives the class a chance * to properly dispose of the result. * to clean up post-cancellation and to properly dispose of the result. * * @param data The value that was returned by {@link #loadInBackground}, or null * if the task threw {@link OperationCanceledException}. */ public void onCanceled(D data) { } Loading Loading @@ -218,6 +232,8 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!"); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mCancellingTask = null; if (DEBUG) Slog.v(TAG, "Delivering cancellation"); deliverCancellation(); executePendingTask(); } } Loading @@ -240,38 +256,72 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { } /** * Called on a worker thread to perform the actual load and to return * the result of the load operation. * * Implementations should not deliver the result directly, but should return them * from this method, which will eventually end up calling {@link #deliverResult} on * the UI thread. If implementations need to process the results on the UI thread * they may override {@link #deliverResult} and do so there. * * To support cancellation, this method should periodically check the value of * {@link #isLoadInBackgroundCanceled} and terminate when it returns true. * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load * directly instead of polling {@link #isLoadInBackgroundCanceled}. * * When the load is canceled, this method may either return normally or throw * {@link OperationCanceledException}. In either case, the {@link Loader} will * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the * result object, if any. * * @return The result of the load operation. * * @throws OperationCanceledException if the load is canceled during execution. * * @see #isLoadInBackgroundCanceled * @see #cancelLoadInBackground * @see #onCanceled */ public abstract D loadInBackground(); /** * Called on a worker thread to perform the actual load. Implementations should not deliver the * result directly, but should return them from this method, which will eventually end up * calling {@link #deliverResult} on the UI thread. If implementations need to process * the results on the UI thread they may override {@link #deliverResult} and do so * there. * Calls {@link #loadInBackground()}. * * This method is reserved for use by the loader framework. * Subclasses should override {@link #loadInBackground} instead of this method. * * @return The result of the load operation. * * @return Implementations must return the result of their load operation. * @throws OperationCanceledException if the load is canceled during execution. * * @see #loadInBackground */ protected D onLoadInBackground() { return loadInBackground(); } /** * Override this method to try to abort the computation currently taking * place on a background thread. * Called on the main thread to abort a load in progress. * * Override this method to abort the current invocation of {@link #loadInBackground} * that is running in the background on a worker thread. * * Note that when this method is called, it is possible that {@link #loadInBackground} * has not started yet or has already completed. * This method should do nothing if {@link #loadInBackground} has not started * running or if it has already finished. * * @see #loadInBackground */ protected void onCancelLoadInBackground() { public void cancelLoadInBackground() { } /** * Returns true if the current execution of {@link #loadInBackground()} is being canceled. * Returns true if the current invocation of {@link #loadInBackground} is being canceled. * * @return True if the current invocation of {@link #loadInBackground} is being canceled. * * @return True if the current execution of {@link #loadInBackground()} is being canceled. * @see #loadInBackground */ protected boolean isLoadInBackgroundCanceled() { public boolean isLoadInBackgroundCanceled() { return mCancellingTask != null; } Loading @@ -288,11 +338,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { public void waitForLoader() { LoadTask task = mTask; if (task != null) { try { task.done.await(); } catch (InterruptedException e) { // Ignore } task.waitForLoader(); } } Loading
core/java/android/content/CursorLoader.java +2 −2 Original line number Diff line number Diff line Loading @@ -76,8 +76,8 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { } @Override protected void onCancelLoadInBackground() { super.onCancelLoadInBackground(); public void cancelLoadInBackground() { super.cancelLoadInBackground(); synchronized (this) { if (mCancelationSignal != null) { Loading
core/java/android/content/Loader.java +101 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import java.io.PrintWriter; public class Loader<D> { int mId; OnLoadCompleteListener<D> mListener; OnLoadCanceledListener<D> mOnLoadCanceledListener; Context mContext; boolean mStarted = false; boolean mAbandoned = false; Loading Loading @@ -99,6 +100,23 @@ public class Loader<D> { public void onLoadComplete(Loader<D> loader, D data); } /** * Interface that is implemented to discover when a Loader has been canceled * before it finished loading its data. You do not normally need to implement * this yourself; it is used in the implementation of {@link android.app.LoaderManager} * to find out when a Loader it is managing has been canceled so that it * can schedule the next Loader. This interface should only be used if a * Loader is not being used in conjunction with LoaderManager. */ public interface OnLoadCanceledListener<D> { /** * Called on the thread that created the Loader when the load is canceled. * * @param loader the loader that canceled the load */ public void onLoadCanceled(Loader<D> loader); } /** * Stores away the application context associated with context. * Since Loaders can be used across multiple activities it's dangerous to Loading Loading @@ -126,6 +144,18 @@ public class Loader<D> { } } /** * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled. * Should only be called by subclasses. * * Must be called from the process's main thread. */ public void deliverCancellation() { if (mOnLoadCanceledListener != null) { mOnLoadCanceledListener.onLoadCanceled(this); } } /** * @return an application context retrieved from the Context passed to the constructor. */ Loading Loading @@ -170,6 +200,40 @@ public class Loader<D> { mListener = null; } /** * Registers a listener that will receive callbacks when a load is canceled. * The callback will be called on the process's main thread so it's safe to * pass the results to widgets. * * Must be called from the process's main thread. * * @param listener The listener to register. */ public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { if (mOnLoadCanceledListener != null) { throw new IllegalStateException("There is already a listener registered"); } mOnLoadCanceledListener = listener; } /** * Unregisters a listener that was previously added with * {@link #registerOnLoadCanceledListener}. * * Must be called from the process's main thread. * * @param listener The listener to unregister. */ public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { if (mOnLoadCanceledListener == null) { throw new IllegalStateException("No listener register"); } if (mOnLoadCanceledListener != listener) { throw new IllegalArgumentException("Attempting to unregister the wrong listener"); } mOnLoadCanceledListener = null; } /** * Return whether this load has been started. That is, its {@link #startLoading()} * has been called and no calls to {@link #stopLoading()} or Loading Loading @@ -233,6 +297,43 @@ public class Loader<D> { protected void onStartLoading() { } /** * Attempt to cancel the current load task. * Must be called on the main thread of the process. * * <p>Cancellation is not an immediate operation, since the load is performed * in a background thread. If there is currently a load in progress, this * method requests that the load be canceled, and notes this is the case; * once the background thread has completed its work its remaining state * will be cleared. If another load request comes in during this time, * it will be held until the canceled load is complete. * * @return Returns <tt>false</tt> if the task could not be canceled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task * is still running and the {@link OnLoadCanceledListener} will be called * when the task completes. */ public boolean cancelLoad() { return onCancelLoad(); } /** * Subclasses must implement this to take care of requests to {@link #cancelLoad()}. * This will always be called from the process's main thread. * * @return Returns <tt>false</tt> if the task could not be canceled, * typically because it has already completed normally, or * because {@link #startLoading()} hasn't been called; returns * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task * is still running and the {@link OnLoadCanceledListener} will be called * when the task completes. */ protected boolean onCancelLoad() { return false; } /** * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously * loaded data set and load a new one. This simply calls through to the Loading