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