• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.database.ContentObserver;
20 import android.os.Handler;
21 import android.util.DebugUtils;
22 
23 import java.io.FileDescriptor;
24 import java.io.PrintWriter;
25 
26 /**
27  * An abstract class that performs asynchronous loading of data. While Loaders are active
28  * they should monitor the source of their data and deliver new results when the contents
29  * change.  See {@link android.app.LoaderManager} for more detail.
30  *
31  * <p><b>Note on threading:</b> Clients of loaders should as a rule perform
32  * any calls on to a Loader from the main thread of their process (that is,
33  * the thread the Activity callbacks and other things occur on).  Subclasses
34  * of Loader (such as {@link AsyncTaskLoader}) will often perform their work
35  * in a separate thread, but when delivering their results this too should
36  * be done on the main thread.</p>
37  *
38  * <p>Subclasses generally must implement at least {@link #onStartLoading()},
39  * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.</p>
40  *
41  * <p>Most implementations should not derive directly from this class, but
42  * instead inherit from {@link AsyncTaskLoader}.</p>
43  *
44  * <div class="special reference">
45  * <h3>Developer Guides</h3>
46  * <p>For more information about using loaders, read the
47  * <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p>
48  * </div>
49  *
50  * @param <D> The result returned when the load is complete
51  */
52 public class Loader<D> {
53     int mId;
54     OnLoadCompleteListener<D> mListener;
55     Context mContext;
56     boolean mStarted = false;
57     boolean mAbandoned = false;
58     boolean mReset = true;
59     boolean mContentChanged = false;
60 
61     public final class ForceLoadContentObserver extends ContentObserver {
ForceLoadContentObserver()62         public ForceLoadContentObserver() {
63             super(new Handler());
64         }
65 
66         @Override
deliverSelfNotifications()67         public boolean deliverSelfNotifications() {
68             return true;
69         }
70 
71         @Override
onChange(boolean selfChange)72         public void onChange(boolean selfChange) {
73             onContentChanged();
74         }
75     }
76 
77     public interface OnLoadCompleteListener<D> {
78         /**
79          * Called on the thread that created the Loader when the load is complete.
80          *
81          * @param loader the loader that completed the load
82          * @param data the result of the load
83          */
onLoadComplete(Loader<D> loader, D data)84         public void onLoadComplete(Loader<D> loader, D data);
85     }
86 
87     /**
88      * Stores away the application context associated with context.
89      * Since Loaders can be used across multiple activities it's dangerous to
90      * store the context directly; always use {@link #getContext()} to retrieve
91      * the Loader's Context, don't use the constructor argument directly.
92      * The Context returned by {@link #getContext} is safe to use across
93      * Activity instances.
94      *
95      * @param context used to retrieve the application context.
96      */
Loader(Context context)97     public Loader(Context context) {
98         mContext = context.getApplicationContext();
99     }
100 
101     /**
102      * Sends the result of the load to the registered listener. Should only be called by subclasses.
103      *
104      * Must be called from the process's main thread.
105      *
106      * @param data the result of the load
107      */
deliverResult(D data)108     public void deliverResult(D data) {
109         if (mListener != null) {
110             mListener.onLoadComplete(this, data);
111         }
112     }
113 
114     /**
115      * @return an application context retrieved from the Context passed to the constructor.
116      */
getContext()117     public Context getContext() {
118         return mContext;
119     }
120 
121     /**
122      * @return the ID of this loader
123      */
getId()124     public int getId() {
125         return mId;
126     }
127 
128     /**
129      * Registers a class that will receive callbacks when a load is complete.
130      * The callback will be called on the process's main thread so it's safe to
131      * pass the results to widgets.
132      *
133      * <p>Must be called from the process's main thread.
134      */
registerListener(int id, OnLoadCompleteListener<D> listener)135     public void registerListener(int id, OnLoadCompleteListener<D> listener) {
136         if (mListener != null) {
137             throw new IllegalStateException("There is already a listener registered");
138         }
139         mListener = listener;
140         mId = id;
141     }
142 
143     /**
144      * Remove a listener that was previously added with {@link #registerListener}.
145      *
146      * Must be called from the process's main thread.
147      */
unregisterListener(OnLoadCompleteListener<D> listener)148     public void unregisterListener(OnLoadCompleteListener<D> listener) {
149         if (mListener == null) {
150             throw new IllegalStateException("No listener register");
151         }
152         if (mListener != listener) {
153             throw new IllegalArgumentException("Attempting to unregister the wrong listener");
154         }
155         mListener = null;
156     }
157 
158     /**
159      * Return whether this load has been started.  That is, its {@link #startLoading()}
160      * has been called and no calls to {@link #stopLoading()} or
161      * {@link #reset()} have yet been made.
162      */
isStarted()163     public boolean isStarted() {
164         return mStarted;
165     }
166 
167     /**
168      * Return whether this loader has been abandoned.  In this state, the
169      * loader <em>must not</em> report any new data, and <em>must</em> keep
170      * its last reported data valid until it is finally reset.
171      */
isAbandoned()172     public boolean isAbandoned() {
173         return mAbandoned;
174     }
175 
176     /**
177      * Return whether this load has been reset.  That is, either the loader
178      * has not yet been started for the first time, or its {@link #reset()}
179      * has been called.
180      */
isReset()181     public boolean isReset() {
182         return mReset;
183     }
184 
185     /**
186      * Starts an asynchronous load of the Loader's data. When the result
187      * is ready the callbacks will be called on the process's main thread.
188      * If a previous load has been completed and is still valid
189      * the result may be passed to the callbacks immediately.
190      * The loader will monitor the source of
191      * the data set and may deliver future callbacks if the source changes.
192      * Calling {@link #stopLoading} will stop the delivery of callbacks.
193      *
194      * <p>This updates the Loader's internal state so that
195      * {@link #isStarted()} and {@link #isReset()} will return the correct
196      * values, and then calls the implementation's {@link #onStartLoading()}.
197      *
198      * <p>Must be called from the process's main thread.
199      */
startLoading()200     public final void startLoading() {
201         mStarted = true;
202         mReset = false;
203         mAbandoned = false;
204         onStartLoading();
205     }
206 
207     /**
208      * Subclasses must implement this to take care of loading their data,
209      * as per {@link #startLoading()}.  This is not called by clients directly,
210      * but as a result of a call to {@link #startLoading()}.
211      */
onStartLoading()212     protected void onStartLoading() {
213     }
214 
215     /**
216      * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
217      * loaded data set and load a new one.  This simply calls through to the
218      * implementation's {@link #onForceLoad()}.  You generally should only call this
219      * when the loader is started -- that is, {@link #isStarted()} returns true.
220      *
221      * <p>Must be called from the process's main thread.
222      */
forceLoad()223     public void forceLoad() {
224         onForceLoad();
225     }
226 
227     /**
228      * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
229      * This will always be called from the process's main thread.
230      */
onForceLoad()231     protected void onForceLoad() {
232     }
233 
234     /**
235      * Stops delivery of updates until the next time {@link #startLoading()} is called.
236      * Implementations should <em>not</em> invalidate their data at this point --
237      * clients are still free to use the last data the loader reported.  They will,
238      * however, typically stop reporting new data if the data changes; they can
239      * still monitor for changes, but must not report them to the client until and
240      * if {@link #startLoading()} is later called.
241      *
242      * <p>This updates the Loader's internal state so that
243      * {@link #isStarted()} will return the correct
244      * value, and then calls the implementation's {@link #onStopLoading()}.
245      *
246      * <p>Must be called from the process's main thread.
247      */
stopLoading()248     public void stopLoading() {
249         mStarted = false;
250         onStopLoading();
251     }
252 
253     /**
254      * Subclasses must implement this to take care of stopping their loader,
255      * as per {@link #stopLoading()}.  This is not called by clients directly,
256      * but as a result of a call to {@link #stopLoading()}.
257      * This will always be called from the process's main thread.
258      */
onStopLoading()259     protected void onStopLoading() {
260     }
261 
262     /**
263      * Tell the Loader that it is being abandoned.  This is called prior
264      * to {@link #reset} to have it retain its current data but not report
265      * any new data.
266      */
abandon()267     public void abandon() {
268         mAbandoned = true;
269         onAbandon();
270     }
271 
272     /**
273      * Subclasses implement this to take care of being abandoned.  This is
274      * an optional intermediate state prior to {@link #onReset()} -- it means that
275      * the client is no longer interested in any new data from the loader,
276      * so the loader must not report any further updates.  However, the
277      * loader <em>must</em> keep its last reported data valid until the final
278      * {@link #onReset()} happens.  You can retrieve the current abandoned
279      * state with {@link #isAbandoned}.
280      */
onAbandon()281     protected void onAbandon() {
282     }
283 
284     /**
285      * Resets the state of the Loader.  The Loader should at this point free
286      * all of its resources, since it may never be called again; however, its
287      * {@link #startLoading()} may later be called at which point it must be
288      * able to start running again.
289      *
290      * <p>This updates the Loader's internal state so that
291      * {@link #isStarted()} and {@link #isReset()} will return the correct
292      * values, and then calls the implementation's {@link #onReset()}.
293      *
294      * <p>Must be called from the process's main thread.
295      */
reset()296     public void reset() {
297         onReset();
298         mReset = true;
299         mStarted = false;
300         mAbandoned = false;
301         mContentChanged = false;
302     }
303 
304     /**
305      * Subclasses must implement this to take care of resetting their loader,
306      * as per {@link #reset()}.  This is not called by clients directly,
307      * but as a result of a call to {@link #reset()}.
308      * This will always be called from the process's main thread.
309      */
onReset()310     protected void onReset() {
311     }
312 
313     /**
314      * Take the current flag indicating whether the loader's content had
315      * changed while it was stopped.  If it had, true is returned and the
316      * flag is cleared.
317      */
takeContentChanged()318     public boolean takeContentChanged() {
319         boolean res = mContentChanged;
320         mContentChanged = false;
321         return res;
322     }
323 
324     /**
325      * Called when {@link ForceLoadContentObserver} detects a change.  The
326      * default implementation checks to see if the loader is currently started;
327      * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag
328      * so that {@link #takeContentChanged()} returns true.
329      *
330      * <p>Must be called from the process's main thread.
331      */
onContentChanged()332     public void onContentChanged() {
333         if (mStarted) {
334             forceLoad();
335         } else {
336             // This loader has been stopped, so we don't want to load
337             // new data right now...  but keep track of it changing to
338             // refresh later if we start again.
339             mContentChanged = true;
340         }
341     }
342 
343     /**
344      * For debugging, converts an instance of the Loader's data class to
345      * a string that can be printed.  Must handle a null data.
346      */
dataToString(D data)347     public String dataToString(D data) {
348         StringBuilder sb = new StringBuilder(64);
349         DebugUtils.buildShortClassTag(data, sb);
350         sb.append("}");
351         return sb.toString();
352     }
353 
354     @Override
toString()355     public String toString() {
356         StringBuilder sb = new StringBuilder(64);
357         DebugUtils.buildShortClassTag(this, sb);
358         sb.append(" id=");
359         sb.append(mId);
360         sb.append("}");
361         return sb.toString();
362     }
363 
364     /**
365      * Print the Loader's state into the given stream.
366      *
367      * @param prefix Text to print at the front of each line.
368      * @param fd The raw file descriptor that the dump is being sent to.
369      * @param writer A PrintWriter to which the dump is to be set.
370      * @param args Additional arguments to the dump request.
371      */
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)372     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
373         writer.print(prefix); writer.print("mId="); writer.print(mId);
374                 writer.print(" mListener="); writer.println(mListener);
375         writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
376                 writer.print(" mContentChanged="); writer.print(mContentChanged);
377                 writer.print(" mAbandoned="); writer.print(mAbandoned);
378                 writer.print(" mReset="); writer.println(mReset);
379     }
380 }