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