blob: 5f8c0984982d1a37b0d7ed778454009483718ceb [file] [log] [blame]
Dianne Hackbornc8017682010-07-06 13:34:38 -07001/*
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.app;
18
19import android.content.Loader;
Dianne Hackbornc8017682010-07-06 13:34:38 -070020import android.os.Bundle;
Dianne Hackborna2ea7472010-12-20 12:10:01 -080021import android.util.DebugUtils;
Dianne Hackborn5e0d5952010-08-05 13:45:35 -070022import android.util.Log;
Dianne Hackbornc8017682010-07-06 13:34:38 -070023import android.util.SparseArray;
24
Dianne Hackborn30d71892010-12-11 10:37:55 -080025import java.io.FileDescriptor;
26import java.io.PrintWriter;
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -080027import java.lang.reflect.Modifier;
Dianne Hackborn30d71892010-12-11 10:37:55 -080028
Dianne Hackbornc8017682010-07-06 13:34:38 -070029/**
Dianne Hackborn4911b782010-07-15 12:54:39 -070030 * Interface associated with an {@link Activity} or {@link Fragment} for managing
Dianne Hackbornc9189352010-12-15 14:57:25 -080031 * one or more {@link android.content.Loader} instances associated with it. This
32 * helps an application manage longer-running operations in conjunction with the
33 * Activity or Fragment lifecycle; the most common use of this is with a
34 * {@link android.content.CursorLoader}, however applications are free to write
35 * their own loaders for loading other types of data.
36 *
37 * <p>As an example, here is the full implementation of a {@link Fragment}
38 * that displays a {@link android.widget.ListView} containing the results of
39 * a query against the contacts content provider. It uses a
40 * {@link android.content.CursorLoader} to manage the query on the provider.
41 *
42 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
43 * fragment_cursor}
Dianne Hackbornc8017682010-07-06 13:34:38 -070044 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -070045public abstract class LoaderManager {
Dianne Hackborn4911b782010-07-15 12:54:39 -070046 /**
47 * Callback interface for a client to interact with the manager.
48 */
49 public interface LoaderCallbacks<D> {
50 /**
51 * Instantiate and return a new Loader for the given ID.
52 *
53 * @param id The ID whose loader is to be created.
54 * @param args Any arguments supplied by the caller.
55 * @return Return a new Loader instance that is ready to start loading.
56 */
57 public Loader<D> onCreateLoader(int id, Bundle args);
58
59 /**
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -070060 * Called when a previously created loader has finished its load. Note
61 * that normally an application is <em>not</em> allowed to commit fragment
62 * transactions while in this call, since it can happen after an
63 * activity's state is saved. See {@link FragmentManager#openTransaction()
64 * FragmentManager.openTransaction()} for further discussion on this.
65 *
Dianne Hackbornc9189352010-12-15 14:57:25 -080066 * <p>This function is guaranteed to be called prior to the release of
67 * the last data that was supplied for this Loader. At this point
68 * you should remove all use of the old data (since it will be released
69 * soon), but should not do your own release of the data since its Loader
70 * owns it and will take care of that. The Loader will take care of
71 * management of its data so you don't have to. In particular:
72 *
73 * <ul>
74 * <li> <p>The Loader will monitor for changes to the data, and report
75 * them to you through new calls here. You should not monitor the
76 * data yourself. For example, if the data is a {@link android.database.Cursor}
77 * and you place it in a {@link android.widget.CursorAdapter}, use
78 * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
79 * android.database.Cursor, int)} constructor <em>without</em> passing
80 * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
81 * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
82 * (that is, use 0 for the flags argument). This prevents the CursorAdapter
83 * from doing its own observing of the Cursor, which is not needed since
84 * when a change happens you will get a new Cursor throw another call
85 * here.
86 * <li> The Loader will release the data once it knows the application
87 * is no longer using it. For example, if the data is
88 * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
89 * you should not call close() on it yourself. If the Cursor is being placed in a
90 * {@link android.widget.CursorAdapter}, you should use the
91 * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
92 * method so that the old Cursor is not closed.
93 * </ul>
94 *
Dianne Hackborn4911b782010-07-15 12:54:39 -070095 * @param loader The Loader that has finished.
96 * @param data The data generated by the Loader.
97 */
98 public void onLoadFinished(Loader<D> loader, D data);
Dianne Hackbornc9189352010-12-15 14:57:25 -080099
100 /**
101 * Called when a previously created loader is being reset, and thus
102 * making its data unavailable. The application should at this point
103 * remove any references it has to the Loader's data.
104 *
105 * @param loader The Loader that is being reset.
106 */
107 public void onLoaderReset(Loader<D> loader);
Dianne Hackborn4911b782010-07-15 12:54:39 -0700108 }
109
110 /**
111 * Ensures a loader is initialized and active. If the loader doesn't
112 * already exist, one is created and (if the activity/fragment is currently
113 * started) starts the loader. Otherwise the last created
114 * loader is re-used.
115 *
116 * <p>In either case, the given callback is associated with the loader, and
117 * will be called as the loader state changes. If at the point of call
118 * the caller is in its started state, and the requested loader
119 * already exists and has generated its data, then
Dianne Hackbornc9189352010-12-15 14:57:25 -0800120 * callback {@link LoaderCallbacks#onLoadFinished} will
Dianne Hackborn4911b782010-07-15 12:54:39 -0700121 * be called immediately (inside of this function), so you must be prepared
122 * for this to happen.
Dianne Hackbornc9189352010-12-15 14:57:25 -0800123 *
124 * @param id A unique identifier for this loader. Can be whatever you want.
125 * Identifiers are scoped to a particular LoaderManager instance.
126 * @param args Optional arguments to supply to the loader at construction.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800127 * If a loader already exists (a new one does not need to be created), this
128 * parameter will be ignored and the last arguments continue to be used.
Dianne Hackbornc9189352010-12-15 14:57:25 -0800129 * @param callback Interface the LoaderManager will call to report about
130 * changes in the state of the loader. Required.
Dianne Hackborn4911b782010-07-15 12:54:39 -0700131 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700132 public abstract <D> Loader<D> initLoader(int id, Bundle args,
Dianne Hackborn4911b782010-07-15 12:54:39 -0700133 LoaderManager.LoaderCallbacks<D> callback);
134
135 /**
Dianne Hackbornc9189352010-12-15 14:57:25 -0800136 * Starts a new or restarts an existing {@link android.content.Loader} in
137 * this manager, registers the callbacks to it,
Dianne Hackborn4911b782010-07-15 12:54:39 -0700138 * and (if the activity/fragment is currently started) starts loading it.
139 * If a loader with the same id has previously been
140 * started it will automatically be destroyed when the new loader completes
141 * its work. The callback will be delivered before the old loader
142 * is destroyed.
Dianne Hackbornc9189352010-12-15 14:57:25 -0800143 *
144 * @param id A unique identifier for this loader. Can be whatever you want.
145 * Identifiers are scoped to a particular LoaderManager instance.
146 * @param args Optional arguments to supply to the loader at construction.
147 * @param callback Interface the LoaderManager will call to report about
148 * changes in the state of the loader. Required.
Dianne Hackborn4911b782010-07-15 12:54:39 -0700149 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700150 public abstract <D> Loader<D> restartLoader(int id, Bundle args,
Dianne Hackborn4911b782010-07-15 12:54:39 -0700151 LoaderManager.LoaderCallbacks<D> callback);
152
153 /**
Dianne Hackbornf73c75c2010-12-17 16:54:05 -0800154 * Stops and removes the loader with the given ID. If this loader
155 * had previously reported data to the client through
156 * {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call
157 * will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}.
Dianne Hackborn4911b782010-07-15 12:54:39 -0700158 */
Dianne Hackbornc9189352010-12-15 14:57:25 -0800159 public abstract void destroyLoader(int id);
160
161 /**
162 * @deprecated Renamed to {@link #destroyLoader}.
163 */
164 @Deprecated
165 public void stopLoader(int id) {
166 destroyLoader(id);
167 }
Dianne Hackborn4911b782010-07-15 12:54:39 -0700168
169 /**
170 * Return the Loader with the given id or null if no matching Loader
171 * is found.
172 */
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700173 public abstract <D> Loader<D> getLoader(int id);
Dianne Hackborn30d71892010-12-11 10:37:55 -0800174
175 /**
176 * Print the LoaderManager's state into the given stream.
177 *
178 * @param prefix Text to print at the front of each line.
179 * @param fd The raw file descriptor that the dump is being sent to.
180 * @param writer A PrintWriter to which the dump is to be set.
181 * @param args Additional arguments to the dump request.
182 */
183 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800184
185 /**
186 * Control whether the framework's internal loader manager debugging
187 * logs are turned on. If enabled, you will see output in logcat as
188 * the framework performs loader operations.
189 */
190 public static void enableDebugLogging(boolean enabled) {
191 LoaderManagerImpl.DEBUG = enabled;
192 }
Dianne Hackborn4911b782010-07-15 12:54:39 -0700193}
194
Dianne Hackbornab36acb2010-11-05 14:12:11 -0700195class LoaderManagerImpl extends LoaderManager {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800196 static final String TAG = "LoaderManager";
197 static boolean DEBUG = true;
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700198
199 // These are the currently active loaders. A loader is here
200 // from the time its load is started until it has been explicitly
201 // stopped or restarted by the application.
Dianne Hackborn2707d602010-07-09 18:01:20 -0700202 final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700203
204 // These are previously run loaders. This list is maintained internally
205 // to avoid destroying a loader while an application is still using it.
206 // It allows an application to restart a loader, but continue using its
207 // previously run loader until the new loader's data is available.
Dianne Hackborn2707d602010-07-09 18:01:20 -0700208 final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700209
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700210 Activity mActivity;
Dianne Hackborn2707d602010-07-09 18:01:20 -0700211 boolean mStarted;
212 boolean mRetaining;
213 boolean mRetainingStarted;
Dianne Hackborn4911b782010-07-15 12:54:39 -0700214
Dianne Hackbornc8017682010-07-06 13:34:38 -0700215 final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700216 final int mId;
217 final Bundle mArgs;
218 LoaderManager.LoaderCallbacks<Object> mCallbacks;
219 Loader<Object> mLoader;
220 Object mData;
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800221 Object mDeliveredData;
Dianne Hackborn2707d602010-07-09 18:01:20 -0700222 boolean mStarted;
223 boolean mRetaining;
224 boolean mRetainingStarted;
225 boolean mDestroyed;
226 boolean mListenerRegistered;
Dianne Hackborn30d71892010-12-11 10:37:55 -0800227
Dianne Hackborn2707d602010-07-09 18:01:20 -0700228 public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
229 mId = id;
230 mArgs = args;
231 mCallbacks = callbacks;
232 }
233
234 void start() {
235 if (mRetaining && mRetainingStarted) {
236 // Our owner is started, but we were being retained from a
237 // previous instance in the started state... so there is really
238 // nothing to do here, since the loaders are still started.
239 mStarted = true;
240 return;
241 }
242
Dianne Hackborn4911b782010-07-15 12:54:39 -0700243 if (mStarted) {
244 // If loader already started, don't restart.
245 return;
246 }
247
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800248 mStarted = true;
249
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700250 if (DEBUG) Log.v(TAG, " Starting: " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700251 if (mLoader == null && mCallbacks != null) {
252 mLoader = mCallbacks.onCreateLoader(mId, mArgs);
253 }
254 if (mLoader != null) {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800255 if (mLoader.getClass().isMemberClass()
256 && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800257 throw new IllegalArgumentException(
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800258 "Object returned from onCreateLoader must not be a non-static inner member class: "
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800259 + mLoader);
260 }
Dianne Hackborn4911b782010-07-15 12:54:39 -0700261 if (!mListenerRegistered) {
262 mLoader.registerListener(mId, this);
263 mListenerRegistered = true;
264 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700265 mLoader.startLoading();
Dianne Hackborn2707d602010-07-09 18:01:20 -0700266 }
267 }
268
269 void retain() {
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700270 if (DEBUG) Log.v(TAG, " Retaining: " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700271 mRetaining = true;
272 mRetainingStarted = mStarted;
273 mStarted = false;
274 mCallbacks = null;
275 }
276
277 void finishRetain() {
278 if (mRetaining) {
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700279 if (DEBUG) Log.v(TAG, " Finished Retaining: " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700280 mRetaining = false;
281 if (mStarted != mRetainingStarted) {
282 if (!mStarted) {
283 // This loader was retained in a started state, but
284 // at the end of retaining everything our owner is
285 // no longer started... so make it stop.
286 stop();
287 }
288 }
Dianne Hackbornc9189352010-12-15 14:57:25 -0800289 }
290
291 if (mStarted && mData != null) {
292 // This loader has retained its data, either completely across
293 // a configuration change or just whatever the last data set
294 // was after being restarted from a stop, and now at the point of
295 // finishing the retain we find we remain started, have
296 // our data, and the owner has a new callback... so
297 // let's deliver the data now.
298 callOnLoadFinished(mLoader, mData);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700299 }
300 }
301
302 void stop() {
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700303 if (DEBUG) Log.v(TAG, " Stopping: " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700304 mStarted = false;
Dianne Hackborndebb2e22010-08-09 16:32:52 -0700305 if (!mRetaining) {
306 if (mLoader != null && mListenerRegistered) {
307 // Let the loader know we're done with it
308 mListenerRegistered = false;
309 mLoader.unregisterListener(this);
310 mLoader.stopLoading();
311 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700312 }
313 }
314
315 void destroy() {
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700316 if (DEBUG) Log.v(TAG, " Destroying: " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700317 mDestroyed = true;
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800318 boolean needReset = mDeliveredData != null;
319 mDeliveredData = null;
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800320 if (mCallbacks != null && mLoader != null && mData != null && needReset) {
321 if (DEBUG) Log.v(TAG, " Reseting: " + this);
Dianne Hackbornc9189352010-12-15 14:57:25 -0800322 String lastBecause = null;
323 if (mActivity != null) {
324 lastBecause = mActivity.mFragments.mNoTransactionsBecause;
325 mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
326 }
327 try {
328 mCallbacks.onLoaderReset(mLoader);
329 } finally {
330 if (mActivity != null) {
331 mActivity.mFragments.mNoTransactionsBecause = lastBecause;
332 }
333 }
334 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700335 mCallbacks = null;
Dianne Hackbornc9189352010-12-15 14:57:25 -0800336 mData = null;
Dianne Hackborn2707d602010-07-09 18:01:20 -0700337 if (mLoader != null) {
338 if (mListenerRegistered) {
339 mListenerRegistered = false;
340 mLoader.unregisterListener(this);
341 }
Dianne Hackbornc9189352010-12-15 14:57:25 -0800342 mLoader.reset();
Dianne Hackborn2707d602010-07-09 18:01:20 -0700343 }
344 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700345
346 @Override public void onLoadComplete(Loader<Object> loader, Object data) {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800347 if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700348
Dianne Hackborn2707d602010-07-09 18:01:20 -0700349 if (mDestroyed) {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800350 if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed");
Dianne Hackborn2707d602010-07-09 18:01:20 -0700351 return;
352 }
353
Dianne Hackbornc8017682010-07-06 13:34:38 -0700354 // Notify of the new data so the app can switch out the old data before
355 // we try to destroy it.
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800356 if (mData != data) {
357 mData = data;
358 if (mStarted) {
359 callOnLoadFinished(loader, data);
360 }
Dianne Hackbornc9189352010-12-15 14:57:25 -0800361 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700362
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800363 //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700364
365 // We have now given the application the new loader with its
366 // loaded data, so it should have stopped using the previous
367 // loader. If there is a previous loader on the inactive list,
368 // clean it up.
Dianne Hackborn2707d602010-07-09 18:01:20 -0700369 LoaderInfo info = mInactiveLoaders.get(mId);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700370 if (info != null && info != this) {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800371 info.mDeliveredData = null;
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700372 info.destroy();
Dianne Hackborn2707d602010-07-09 18:01:20 -0700373 mInactiveLoaders.remove(mId);
Dianne Hackbornc8017682010-07-06 13:34:38 -0700374 }
375 }
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700376
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700377 void callOnLoadFinished(Loader<Object> loader, Object data) {
378 if (mCallbacks != null) {
379 String lastBecause = null;
380 if (mActivity != null) {
381 lastBecause = mActivity.mFragments.mNoTransactionsBecause;
382 mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
383 }
384 try {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800385 if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
386 + loader.dataToString(data));
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700387 mCallbacks.onLoadFinished(loader, data);
388 } finally {
389 if (mActivity != null) {
390 mActivity.mFragments.mNoTransactionsBecause = lastBecause;
391 }
392 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800393 mDeliveredData = data;
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700394 }
395 }
396
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700397 @Override
398 public String toString() {
399 StringBuilder sb = new StringBuilder(64);
400 sb.append("LoaderInfo{");
401 sb.append(Integer.toHexString(System.identityHashCode(this)));
402 sb.append(" #");
403 sb.append(mId);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800404 sb.append(" : ");
405 DebugUtils.buildShortClassTag(mLoader, sb);
406 sb.append("}}");
Dianne Hackborn30d71892010-12-11 10:37:55 -0800407 return sb.toString();
408 }
409
410 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
411 writer.print(prefix); writer.print("mId="); writer.print(mId);
412 writer.print(" mArgs="); writer.println(mArgs);
413 writer.print(prefix); writer.print("mCallbacks="); writer.println(mCallbacks);
414 writer.print(prefix); writer.print("mLoader="); writer.println(mLoader);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800415 if (mLoader != null) {
416 mLoader.dump(prefix + " ", fd, writer, args);
417 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800418 writer.print(prefix); writer.print("mData="); writer.println(mData);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800419 writer.print(prefix); writer.print("mDeliveredData="); writer.println(mDeliveredData);
Dianne Hackborn30d71892010-12-11 10:37:55 -0800420 writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
421 writer.print(" mRetaining="); writer.print(mRetaining);
Dianne Hackbornf73c75c2010-12-17 16:54:05 -0800422 writer.print(" mDestroyed="); writer.println(mDestroyed);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800423 writer.print(prefix); writer.print("mListenerRegistered=");
424 writer.println(mListenerRegistered);
Dianne Hackborn30d71892010-12-11 10:37:55 -0800425 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700426 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700427
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700428 LoaderManagerImpl(Activity activity, boolean started) {
429 mActivity = activity;
Dianne Hackbornc8017682010-07-06 13:34:38 -0700430 mStarted = started;
431 }
432
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700433 void updateActivity(Activity activity) {
434 mActivity = activity;
435 }
436
Dianne Hackborn2707d602010-07-09 18:01:20 -0700437 private LoaderInfo createLoader(int id, Bundle args,
438 LoaderManager.LoaderCallbacks<Object> callback) {
439 LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
440 mLoaders.put(id, info);
441 Loader<Object> loader = callback.onCreateLoader(id, args);
442 info.mLoader = (Loader<Object>)loader;
443 if (mStarted) {
Dianne Hackborn4911b782010-07-15 12:54:39 -0700444 // The activity will start all existing loaders in it's onStart(),
445 // so only start them here if we're past that point of the activitiy's
446 // life cycle
447 info.start();
Dianne Hackborn2707d602010-07-09 18:01:20 -0700448 }
449 return info;
450 }
451
Dianne Hackborn2707d602010-07-09 18:01:20 -0700452 @SuppressWarnings("unchecked")
453 public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
454 LoaderInfo info = mLoaders.get(id);
455
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800456 if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700457
Dianne Hackborn2707d602010-07-09 18:01:20 -0700458 if (info == null) {
459 // Loader doesn't already exist; create.
460 info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800461 if (DEBUG) Log.v(TAG, " Created new loader " + info);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700462 } else {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800463 if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700464 info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
465 }
466
467 if (info.mData != null && mStarted) {
468 // If the loader has already generated its data, report it now.
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700469 info.callOnLoadFinished(info.mLoader, info.mData);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700470 }
471
472 return (Loader<D>)info.mLoader;
473 }
474
Dianne Hackbornc8017682010-07-06 13:34:38 -0700475 @SuppressWarnings("unchecked")
Dianne Hackborn2707d602010-07-09 18:01:20 -0700476 public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
Dianne Hackbornc8017682010-07-06 13:34:38 -0700477 LoaderInfo info = mLoaders.get(id);
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800478 if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
Dianne Hackbornc8017682010-07-06 13:34:38 -0700479 if (info != null) {
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700480 LoaderInfo inactive = mInactiveLoaders.get(id);
481 if (inactive != null) {
482 if (info.mData != null) {
483 // This loader now has data... we are probably being
484 // called from within onLoadComplete, where we haven't
485 // yet destroyed the last inactive loader. So just do
486 // that now.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800487 if (DEBUG) Log.v(TAG, " Removing last inactive loader: " + info);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800488 inactive.mDeliveredData = null;
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700489 inactive.destroy();
490 mInactiveLoaders.put(id, info);
491 } else {
492 // We already have an inactive loader for this ID that we are
493 // waiting for! Now we have three active loaders... let's just
494 // drop the one in the middle, since we are still waiting for
495 // its result but that result is already out of date.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800496 if (DEBUG) Log.v(TAG, " Removing intermediate loader: " + info);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700497 info.destroy();
498 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700499 } else {
500 // Keep track of the previous instance of this loader so we can destroy
501 // it when the new one completes.
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800502 if (DEBUG) Log.v(TAG, " Making last loader inactive: " + info);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700503 mInactiveLoaders.put(id, info);
504 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700505 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700506
Dianne Hackborn2707d602010-07-09 18:01:20 -0700507 info = createLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
508 return (Loader<D>)info.mLoader;
Dianne Hackbornc8017682010-07-06 13:34:38 -0700509 }
510
Dianne Hackbornc9189352010-12-15 14:57:25 -0800511 public void destroyLoader(int id) {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800512 if (DEBUG) Log.v(TAG, "destroyLoader in " + this + " of " + id);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700513 int idx = mLoaders.indexOfKey(id);
514 if (idx >= 0) {
515 LoaderInfo info = mLoaders.valueAt(idx);
516 mLoaders.removeAt(idx);
Dianne Hackborn4911b782010-07-15 12:54:39 -0700517 info.destroy();
Dianne Hackbornc8017682010-07-06 13:34:38 -0700518 }
Dianne Hackbornf73c75c2010-12-17 16:54:05 -0800519 idx = mInactiveLoaders.indexOfKey(id);
520 if (idx >= 0) {
521 LoaderInfo info = mInactiveLoaders.valueAt(idx);
522 mInactiveLoaders.removeAt(idx);
523 info.destroy();
524 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700525 }
526
Dianne Hackbornc8017682010-07-06 13:34:38 -0700527 @SuppressWarnings("unchecked")
528 public <D> Loader<D> getLoader(int id) {
529 LoaderInfo loaderInfo = mLoaders.get(id);
530 if (loaderInfo != null) {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700531 return (Loader<D>)mLoaders.get(id).mLoader;
Dianne Hackbornc8017682010-07-06 13:34:38 -0700532 }
533 return null;
534 }
535
536 void doStart() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800537 if (DEBUG) Log.v(TAG, "Starting in " + this);
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700538 if (mStarted) {
539 RuntimeException e = new RuntimeException("here");
540 e.fillInStackTrace();
541 Log.w(TAG, "Called doStart when already started: " + this, e);
542 return;
543 }
544
Dianne Hackbornc9189352010-12-15 14:57:25 -0800545 mStarted = true;
546
Dianne Hackbornc8017682010-07-06 13:34:38 -0700547 // Call out to sub classes so they can start their loaders
548 // Let the existing loaders know that we want to be notified when a load is complete
549 for (int i = mLoaders.size()-1; i >= 0; i--) {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700550 mLoaders.valueAt(i).start();
Dianne Hackbornc8017682010-07-06 13:34:38 -0700551 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700552 }
553
554 void doStop() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800555 if (DEBUG) Log.v(TAG, "Stopping in " + this);
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700556 if (!mStarted) {
557 RuntimeException e = new RuntimeException("here");
558 e.fillInStackTrace();
559 Log.w(TAG, "Called doStop when not started: " + this, e);
560 return;
561 }
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700562
Dianne Hackbornc8017682010-07-06 13:34:38 -0700563 for (int i = mLoaders.size()-1; i >= 0; i--) {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700564 mLoaders.valueAt(i).stop();
Dianne Hackbornc8017682010-07-06 13:34:38 -0700565 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700566 mStarted = false;
567 }
568
Dianne Hackborn2707d602010-07-09 18:01:20 -0700569 void doRetain() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800570 if (DEBUG) Log.v(TAG, "Retaining in " + this);
Dianne Hackbornfb3cffe2010-10-25 17:08:56 -0700571 if (!mStarted) {
572 RuntimeException e = new RuntimeException("here");
573 e.fillInStackTrace();
574 Log.w(TAG, "Called doRetain when not started: " + this, e);
575 return;
576 }
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700577
Dianne Hackborn2707d602010-07-09 18:01:20 -0700578 mRetaining = true;
579 mStarted = false;
580 for (int i = mLoaders.size()-1; i >= 0; i--) {
581 mLoaders.valueAt(i).retain();
582 }
583 }
584
585 void finishRetain() {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800586 if (mRetaining) {
587 if (DEBUG) Log.v(TAG, "Finished Retaining in " + this);
Dianne Hackborn5e0d5952010-08-05 13:45:35 -0700588
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800589 mRetaining = false;
590 for (int i = mLoaders.size()-1; i >= 0; i--) {
591 mLoaders.valueAt(i).finishRetain();
592 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700593 }
594 }
595
Dianne Hackbornc8017682010-07-06 13:34:38 -0700596 void doDestroy() {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700597 if (!mRetaining) {
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800598 if (DEBUG) Log.v(TAG, "Destroying Active in " + this);
Dianne Hackbornc8017682010-07-06 13:34:38 -0700599 for (int i = mLoaders.size()-1; i >= 0; i--) {
Dianne Hackborn2707d602010-07-09 18:01:20 -0700600 mLoaders.valueAt(i).destroy();
Dianne Hackbornc8017682010-07-06 13:34:38 -0700601 }
602 }
Dianne Hackborn2707d602010-07-09 18:01:20 -0700603
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800604 if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);
Dianne Hackborn2707d602010-07-09 18:01:20 -0700605 for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
606 mInactiveLoaders.valueAt(i).destroy();
607 }
608 mInactiveLoaders.clear();
Dianne Hackbornc8017682010-07-06 13:34:38 -0700609 }
Dianne Hackborn30d71892010-12-11 10:37:55 -0800610
611 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800612 public String toString() {
613 StringBuilder sb = new StringBuilder(128);
614 sb.append("LoaderManager{");
615 sb.append(Integer.toHexString(System.identityHashCode(this)));
616 sb.append(" in ");
617 DebugUtils.buildShortClassTag(mActivity, sb);
618 sb.append("}}");
619 return sb.toString();
620 }
621
622 @Override
Dianne Hackborn30d71892010-12-11 10:37:55 -0800623 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
624 if (mLoaders.size() > 0) {
625 writer.print(prefix); writer.println("Active Loaders:");
626 String innerPrefix = prefix + " ";
627 for (int i=0; i < mLoaders.size(); i++) {
628 LoaderInfo li = mLoaders.valueAt(i);
629 writer.print(prefix); writer.print(" #"); writer.print(mLoaders.keyAt(i));
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800630 writer.print(": "); writer.println(li.toString());
Dianne Hackborn30d71892010-12-11 10:37:55 -0800631 li.dump(innerPrefix, fd, writer, args);
632 }
633 }
634 if (mInactiveLoaders.size() > 0) {
635 writer.print(prefix); writer.println("Inactive Loaders:");
636 String innerPrefix = prefix + " ";
637 for (int i=0; i < mInactiveLoaders.size(); i++) {
638 LoaderInfo li = mInactiveLoaders.valueAt(i);
639 writer.print(prefix); writer.print(" #"); writer.print(mInactiveLoaders.keyAt(i));
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800640 writer.print(": "); writer.println(li.toString());
Dianne Hackborn30d71892010-12-11 10:37:55 -0800641 li.dump(innerPrefix, fd, writer, args);
642 }
643 }
644 }
Dianne Hackbornc8017682010-07-06 13:34:38 -0700645}