1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.AsyncTask;
21 import android.os.Handler;
22 import android.os.OperationCanceledException;
23 import android.os.SystemClock;
24 import android.util.Log;
25 import android.util.TimeUtils;
26 
27 import java.io.FileDescriptor;
28 import java.io.PrintWriter;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.Executor;
31 
32 /**
33  * Abstract Loader that provides an {@link AsyncTask} to do the work.  See
34  * {@link Loader} and {@link android.app.LoaderManager} for more details.
35  *
36  * <p>Here is an example implementation of an AsyncTaskLoader subclass that
37  * loads the currently installed applications from the package manager.  This
38  * implementation takes care of retrieving the application labels and sorting
39  * its result set from them, monitoring for changes to the installed
40  * applications, and rebuilding the list when a change in configuration requires
41  * this (such as a locale change).
42  *
43  * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
44  *      loader}
45  *
46  * <p>An example implementation of a fragment that uses the above loader to show
47  * the currently installed applications in a list is below.
48  *
49  * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
50  *      fragment}
51  *
52  * @param <D> the data type to be loaded.
53  *
54  * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
55  *      {@link android.support.v4.content.AsyncTaskLoader}
56  */
57 @Deprecated
58 public abstract class AsyncTaskLoader<D> extends Loader<D> {
59     static final String TAG = "AsyncTaskLoader";
60     static final boolean DEBUG = false;
61 
62     final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
63         private final CountDownLatch mDone = new CountDownLatch(1);
64 
65         // Set to true to indicate that the task has been posted to a handler for
66         // execution at a later time.  Used to throttle updates.
67         boolean waiting;
68 
69         /* Runs on a worker thread */
70         @Override
doInBackground(Void... params)71         protected D doInBackground(Void... params) {
72             if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
73             try {
74                 D data = AsyncTaskLoader.this.onLoadInBackground();
75                 if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
76                 return data;
77             } catch (OperationCanceledException ex) {
78                 if (!isCancelled()) {
79                     // onLoadInBackground threw a canceled exception spuriously.
80                     // This is problematic because it means that the LoaderManager did not
81                     // cancel the Loader itself and still expects to receive a result.
82                     // Additionally, the Loader's own state will not have been updated to
83                     // reflect the fact that the task was being canceled.
84                     // So we treat this case as an unhandled exception.
85                     throw ex;
86                 }
87                 if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
88                 return null;
89             }
90         }
91 
92         /* Runs on the UI thread */
93         @Override
onPostExecute(D data)94         protected void onPostExecute(D data) {
95             if (DEBUG) Log.v(TAG, this + " onPostExecute");
96             try {
97                 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
98             } finally {
99                 mDone.countDown();
100             }
101         }
102 
103         /* Runs on the UI thread */
104         @Override
onCancelled(D data)105         protected void onCancelled(D data) {
106             if (DEBUG) Log.v(TAG, this + " onCancelled");
107             try {
108                 AsyncTaskLoader.this.dispatchOnCancelled(this, data);
109             } finally {
110                 mDone.countDown();
111             }
112         }
113 
114         /* Runs on the UI thread, when the waiting task is posted to a handler.
115          * This method is only executed when task execution was deferred (waiting was true). */
116         @Override
run()117         public void run() {
118             waiting = false;
119             AsyncTaskLoader.this.executePendingTask();
120         }
121 
122         /* Used for testing purposes to wait for the task to complete. */
waitForLoader()123         public void waitForLoader() {
124             try {
125                 mDone.await();
126             } catch (InterruptedException e) {
127                 // Ignore
128             }
129         }
130     }
131 
132     @UnsupportedAppUsage
133     private final Executor mExecutor;
134 
135     volatile LoadTask mTask;
136     volatile LoadTask mCancellingTask;
137 
138     long mUpdateThrottle;
139     long mLastLoadCompleteTime = -10000;
140     Handler mHandler;
141 
AsyncTaskLoader(Context context)142     public AsyncTaskLoader(Context context) {
143         this(context, AsyncTask.THREAD_POOL_EXECUTOR);
144     }
145 
146     /** {@hide} */
AsyncTaskLoader(Context context, Executor executor)147     public AsyncTaskLoader(Context context, Executor executor) {
148         super(context);
149         mExecutor = executor;
150     }
151 
152     /**
153      * Set amount to throttle updates by.  This is the minimum time from
154      * when the last {@link #loadInBackground()} call has completed until
155      * a new load is scheduled.
156      *
157      * @param delayMS Amount of delay, in milliseconds.
158      */
setUpdateThrottle(long delayMS)159     public void setUpdateThrottle(long delayMS) {
160         mUpdateThrottle = delayMS;
161         if (delayMS != 0) {
162             mHandler = new Handler();
163         }
164     }
165 
166     @Override
onForceLoad()167     protected void onForceLoad() {
168         super.onForceLoad();
169         cancelLoad();
170         mTask = new LoadTask();
171         if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
172         executePendingTask();
173     }
174 
175     @Override
onCancelLoad()176     protected boolean onCancelLoad() {
177         if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
178         if (mTask != null) {
179             if (!mStarted) {
180                 mContentChanged = true;
181             }
182             if (mCancellingTask != null) {
183                 // There was a pending task already waiting for a previous
184                 // one being canceled; just drop it.
185                 if (DEBUG) Log.v(TAG,
186                         "cancelLoad: still waiting for cancelled task; dropping next");
187                 if (mTask.waiting) {
188                     mTask.waiting = false;
189                     mHandler.removeCallbacks(mTask);
190                 }
191                 mTask = null;
192                 return false;
193             } else if (mTask.waiting) {
194                 // There is a task, but it is waiting for the time it should
195                 // execute.  We can just toss it.
196                 if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
197                 mTask.waiting = false;
198                 mHandler.removeCallbacks(mTask);
199                 mTask = null;
200                 return false;
201             } else {
202                 boolean cancelled = mTask.cancel(false);
203                 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
204                 if (cancelled) {
205                     mCancellingTask = mTask;
206                     cancelLoadInBackground();
207                 }
208                 mTask = null;
209                 return cancelled;
210             }
211         }
212         return false;
213     }
214 
215     /**
216      * Called if the task was canceled before it was completed.  Gives the class a chance
217      * to clean up post-cancellation and to properly dispose of the result.
218      *
219      * @param data The value that was returned by {@link #loadInBackground}, or null
220      * if the task threw {@link OperationCanceledException}.
221      */
onCanceled(D data)222     public void onCanceled(D data) {
223     }
224 
executePendingTask()225     void executePendingTask() {
226         if (mCancellingTask == null && mTask != null) {
227             if (mTask.waiting) {
228                 mTask.waiting = false;
229                 mHandler.removeCallbacks(mTask);
230             }
231             if (mUpdateThrottle > 0) {
232                 long now = SystemClock.uptimeMillis();
233                 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
234                     // Not yet time to do another load.
235                     if (DEBUG) Log.v(TAG, "Waiting until "
236                             + (mLastLoadCompleteTime+mUpdateThrottle)
237                             + " to execute: " + mTask);
238                     mTask.waiting = true;
239                     mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
240                     return;
241                 }
242             }
243             if (DEBUG) Log.v(TAG, "Executing: " + mTask);
244             mTask.executeOnExecutor(mExecutor, (Void[]) null);
245         }
246     }
247 
dispatchOnCancelled(LoadTask task, D data)248     void dispatchOnCancelled(LoadTask task, D data) {
249         onCanceled(data);
250         if (mCancellingTask == task) {
251             if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
252             rollbackContentChanged();
253             mLastLoadCompleteTime = SystemClock.uptimeMillis();
254             mCancellingTask = null;
255             if (DEBUG) Log.v(TAG, "Delivering cancellation");
256             deliverCancellation();
257             executePendingTask();
258         }
259     }
260 
dispatchOnLoadComplete(LoadTask task, D data)261     void dispatchOnLoadComplete(LoadTask task, D data) {
262         if (mTask != task) {
263             if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
264             dispatchOnCancelled(task, data);
265         } else {
266             if (isAbandoned()) {
267                 // This cursor has been abandoned; just cancel the new data.
268                 onCanceled(data);
269             } else {
270                 commitContentChanged();
271                 mLastLoadCompleteTime = SystemClock.uptimeMillis();
272                 mTask = null;
273                 if (DEBUG) Log.v(TAG, "Delivering result");
274                 deliverResult(data);
275             }
276         }
277     }
278 
279     /**
280      * Called on a worker thread to perform the actual load and to return
281      * the result of the load operation.
282      *
283      * Implementations should not deliver the result directly, but should return them
284      * from this method, which will eventually end up calling {@link #deliverResult} on
285      * the UI thread.  If implementations need to process the results on the UI thread
286      * they may override {@link #deliverResult} and do so there.
287      *
288      * To support cancellation, this method should periodically check the value of
289      * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
290      * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
291      * directly instead of polling {@link #isLoadInBackgroundCanceled}.
292      *
293      * When the load is canceled, this method may either return normally or throw
294      * {@link OperationCanceledException}.  In either case, the {@link Loader} will
295      * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
296      * result object, if any.
297      *
298      * @return The result of the load operation.
299      *
300      * @throws OperationCanceledException if the load is canceled during execution.
301      *
302      * @see #isLoadInBackgroundCanceled
303      * @see #cancelLoadInBackground
304      * @see #onCanceled
305      */
loadInBackground()306     public abstract D loadInBackground();
307 
308     /**
309      * Calls {@link #loadInBackground()}.
310      *
311      * This method is reserved for use by the loader framework.
312      * Subclasses should override {@link #loadInBackground} instead of this method.
313      *
314      * @return The result of the load operation.
315      *
316      * @throws OperationCanceledException if the load is canceled during execution.
317      *
318      * @see #loadInBackground
319      */
onLoadInBackground()320     protected D onLoadInBackground() {
321         return loadInBackground();
322     }
323 
324     /**
325      * Called on the main thread to abort a load in progress.
326      *
327      * Override this method to abort the current invocation of {@link #loadInBackground}
328      * that is running in the background on a worker thread.
329      *
330      * This method should do nothing if {@link #loadInBackground} has not started
331      * running or if it has already finished.
332      *
333      * @see #loadInBackground
334      */
cancelLoadInBackground()335     public void cancelLoadInBackground() {
336     }
337 
338     /**
339      * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
340      *
341      * @return True if the current invocation of {@link #loadInBackground} is being canceled.
342      *
343      * @see #loadInBackground
344      */
isLoadInBackgroundCanceled()345     public boolean isLoadInBackgroundCanceled() {
346         return mCancellingTask != null;
347     }
348 
349     /**
350      * Locks the current thread until the loader completes the current load
351      * operation. Returns immediately if there is no load operation running.
352      * Should not be called from the UI thread: calling it from the UI
353      * thread would cause a deadlock.
354      * <p>
355      * Use for testing only.  <b>Never</b> call this from a UI thread.
356      *
357      * @hide
358      */
359     @UnsupportedAppUsage
waitForLoader()360     public void waitForLoader() {
361         LoadTask task = mTask;
362         if (task != null) {
363             task.waitForLoader();
364         }
365     }
366 
367     @Override
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)368     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
369         super.dump(prefix, fd, writer, args);
370         if (mTask != null) {
371             writer.print(prefix); writer.print("mTask="); writer.print(mTask);
372                     writer.print(" waiting="); writer.println(mTask.waiting);
373         }
374         if (mCancellingTask != null) {
375             writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
376                     writer.print(" waiting="); writer.println(mCancellingTask.waiting);
377         }
378         if (mUpdateThrottle != 0) {
379             writer.print(prefix); writer.print("mUpdateThrottle=");
380                     TimeUtils.formatDuration(mUpdateThrottle, writer);
381                     writer.print(" mLastLoadCompleteTime=");
382                     TimeUtils.formatDuration(mLastLoadCompleteTime,
383                             SystemClock.uptimeMillis(), writer);
384                     writer.println();
385         }
386     }
387 }
388