blob: b962800da9156576e7a38f1198fd59e760e0a50e [file] [log] [blame]
Jeff Hamilton9911b7f2010-05-15 02:20:31 -05001/*
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;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080021import android.util.DebugUtils;
22
23import java.io.FileDescriptor;
24import java.io.PrintWriter;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050025
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
Dianne Hackborn9567a662011-04-19 18:44:03 -070029 * change. See {@link android.app.LoaderManager} for more detail.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050030 *
Dianne Hackborn247fe742011-01-08 17:25:57 -080031 * <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 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -080038 * <p>Subclasses generally must implement at least {@link #onStartLoading()},
Dianne Hackborn9567a662011-04-19 18:44:03 -070039 * {@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>
Joe Fernandezb54e7a32011-10-03 15:09:50 -070043 *
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>
Dianne Hackborna2ea7472010-12-20 12:10:01 -080049 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050050 * @param <D> The result returned when the load is complete
51 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -080052public class Loader<D> {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050053 int mId;
54 OnLoadCompleteListener<D> mListener;
55 Context mContext;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080056 boolean mStarted = false;
Dianne Hackborn260c3c72011-01-30 16:55:55 -080057 boolean mAbandoned = false;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080058 boolean mReset = true;
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -080059 boolean mContentChanged = false;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050060
61 public final class ForceLoadContentObserver extends ContentObserver {
62 public ForceLoadContentObserver() {
63 super(new Handler());
64 }
65
66 @Override
67 public boolean deliverSelfNotifications() {
68 return true;
69 }
70
71 @Override
72 public void onChange(boolean selfChange) {
Makoto Onukiec37c882010-08-02 16:57:01 -070073 onContentChanged();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050074 }
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 */
84 public void onLoadComplete(Loader<D> loader, D data);
85 }
86
87 /**
Dianne Hackborn9567a662011-04-19 18:44:03 -070088 * 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.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050094 *
95 * @param context used to retrieve the application context.
96 */
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 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800104 * Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500105 *
106 * @param data the result of the load
107 */
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 */
117 public Context getContext() {
118 return mContext;
119 }
120
121 /**
122 * @return the ID of this loader
123 */
124 public int getId() {
125 return mId;
126 }
127
128 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800129 * 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.
Makoto Onukiec37c882010-08-02 16:57:01 -0700132 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800133 * <p>Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500134 */
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 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800144 * Remove a listener that was previously added with {@link #registerListener}.
145 *
146 * Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500147 */
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 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800159 * 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 */
163 public boolean isStarted() {
164 return mStarted;
165 }
166
167 /**
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800168 * 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 */
172 public boolean isAbandoned() {
173 return mAbandoned;
174 }
175
176 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800177 * 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 */
181 public boolean isReset() {
182 return mReset;
183 }
184
185 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800186 * 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.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500193 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800194 * <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 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800198 * <p>Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500199 */
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800200 public final void startLoading() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800201 mStarted = true;
202 mReset = false;
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800203 mAbandoned = false;
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800204 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 */
212 protected void onStartLoading() {
213 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500214
215 /**
216 * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800217 * loaded data set and load a new one. This simply calls through to the
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800218 * implementation's {@link #onForceLoad()}. You generally should only call this
219 * when the loader is started -- that is, {@link #isStarted()} returns true.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800220 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800221 * <p>Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500222 */
Dianne Hackborn247fe742011-01-08 17:25:57 -0800223 public void forceLoad() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800224 onForceLoad();
225 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500226
227 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800228 * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
Dianne Hackborn247fe742011-01-08 17:25:57 -0800229 * This will always be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500230 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800231 protected void onForceLoad() {
232 }
233
234 /**
235 * Stops delivery of updates until the next time {@link #startLoading()} is called.
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800236 * 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.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800241 *
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 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800246 * <p>Must be called from the process's main thread.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800247 */
Dianne Hackborn247fe742011-01-08 17:25:57 -0800248 public void stopLoading() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800249 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()}.
Dianne Hackborn247fe742011-01-08 17:25:57 -0800257 * This will always be called from the process's main thread.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800258 */
259 protected void onStopLoading() {
260 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500261
262 /**
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800263 * 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 */
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 */
281 protected void onAbandon() {
282 }
283
284 /**
Dianne Hackbornc9189352010-12-15 14:57:25 -0800285 * 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.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500289 *
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800290 * <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 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800294 * <p>Must be called from the process's main thread.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500295 */
Dianne Hackborn247fe742011-01-08 17:25:57 -0800296 public void reset() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800297 onReset();
298 mReset = true;
299 mStarted = false;
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800300 mAbandoned = false;
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800301 mContentChanged = false;
Dianne Hackbornc9189352010-12-15 14:57:25 -0800302 }
303
304 /**
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800305 * 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()}.
Dianne Hackborn247fe742011-01-08 17:25:57 -0800308 * This will always be called from the process's main thread.
Dianne Hackbornc9189352010-12-15 14:57:25 -0800309 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800310 protected void onReset() {
Dianne Hackbornc9189352010-12-15 14:57:25 -0800311 }
Makoto Onukiec37c882010-08-02 16:57:01 -0700312
313 /**
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800314 * 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 */
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.
Makoto Onukiec37c882010-08-02 16:57:01 -0700329 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800330 * <p>Must be called from the process's main thread.
Makoto Onukiec37c882010-08-02 16:57:01 -0700331 */
332 public void onContentChanged() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800333 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 }
Makoto Onukiec37c882010-08-02 16:57:01 -0700341 }
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800342
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 */
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
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 */
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);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800376 writer.print(" mContentChanged="); writer.print(mContentChanged);
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800377 writer.print(" mAbandoned="); writer.print(mAbandoned);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800378 writer.print(" mReset="); writer.println(mReset);
379 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500380}