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 * A 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 OnLoadCanceledListener<D> mOnLoadCanceledListener; 56 Context mContext; 57 boolean mStarted = false; 58 boolean mAbandoned = false; 59 boolean mReset = true; 60 boolean mContentChanged = false; 61 boolean mProcessingChange = false; 62 63 /** 64 * An implementation of a ContentObserver that takes care of connecting 65 * it to the Loader to have the loader re-load its data when the observer 66 * is told it has changed. You do not normally need to use this yourself; 67 * it is used for you by {@link CursorLoader} to take care of executing 68 * an update when the cursor's backing data changes. 69 */ 70 public final class ForceLoadContentObserver extends ContentObserver { 71 public ForceLoadContentObserver() { 72 super(new Handler()); 73 } 74 75 @Override 76 public boolean deliverSelfNotifications() { 77 return true; 78 } 79 80 @Override 81 public void onChange(boolean selfChange) { 82 onContentChanged(); 83 } 84 } 85 86 /** 87 * Interface that is implemented to discover when a Loader has finished 88 * loading its data. You do not normally need to implement this yourself; 89 * it is used in the implementation of {@link android.app.LoaderManager} 90 * to find out when a Loader it is managing has completed so that this can 91 * be reported to its client. This interface should only be used if a 92 * Loader is not being used in conjunction with LoaderManager. 93 */ 94 public interface OnLoadCompleteListener<D> { 95 /** 96 * Called on the thread that created the Loader when the load is complete. 97 * 98 * @param loader the loader that completed the load 99 * @param data the result of the load 100 */ 101 public void onLoadComplete(Loader<D> loader, D data); 102 } 103 104 /** 105 * Interface that is implemented to discover when a Loader has been canceled 106 * before it finished loading its data. You do not normally need to implement 107 * this yourself; it is used in the implementation of {@link android.app.LoaderManager} 108 * to find out when a Loader it is managing has been canceled so that it 109 * can schedule the next Loader. This interface should only be used if a 110 * Loader is not being used in conjunction with LoaderManager. 111 */ 112 public interface OnLoadCanceledListener<D> { 113 /** 114 * Called on the thread that created the Loader when the load is canceled. 115 * 116 * @param loader the loader that canceled the load 117 */ 118 public void onLoadCanceled(Loader<D> loader); 119 } 120 121 /** 122 * Stores away the application context associated with context. 123 * Since Loaders can be used across multiple activities it's dangerous to 124 * store the context directly; always use {@link #getContext()} to retrieve 125 * the Loader's Context, don't use the constructor argument directly. 126 * The Context returned by {@link #getContext} is safe to use across 127 * Activity instances. 128 * 129 * @param context used to retrieve the application context. 130 */ 131 public Loader(Context context) { 132 mContext = context.getApplicationContext(); 133 } 134 135 /** 136 * Sends the result of the load to the registered listener. Should only be called by subclasses. 137 * 138 * Must be called from the process's main thread. 139 * 140 * @param data the result of the load 141 */ 142 public void deliverResult(D data) { 143 if (mListener != null) { 144 mListener.onLoadComplete(this, data); 145 } 146 } 147 148 /** 149 * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled. 150 * Should only be called by subclasses. 151 * 152 * Must be called from the process's main thread. 153 */ 154 public void deliverCancellation() { 155 if (mOnLoadCanceledListener != null) { 156 mOnLoadCanceledListener.onLoadCanceled(this); 157 } 158 } 159 160 /** 161 * @return an application context retrieved from the Context passed to the constructor. 162 */ 163 public Context getContext() { 164 return mContext; 165 } 166 167 /** 168 * @return the ID of this loader 169 */ 170 public int getId() { 171 return mId; 172 } 173 174 /** 175 * Registers a class that will receive callbacks when a load is complete. 176 * The callback will be called on the process's main thread so it's safe to 177 * pass the results to widgets. 178 * 179 * <p>Must be called from the process's main thread. 180 */ 181 public void registerListener(int id, OnLoadCompleteListener<D> listener) { 182 if (mListener != null) { 183 throw new IllegalStateException("There is already a listener registered"); 184 } 185 mListener = listener; 186 mId = id; 187 } 188 189 /** 190 * Remove a listener that was previously added with {@link #registerListener}. 191 * 192 * Must be called from the process's main thread. 193 */ 194 public void unregisterListener(OnLoadCompleteListener<D> listener) { 195 if (mListener == null) { 196 throw new IllegalStateException("No listener register"); 197 } 198 if (mListener != listener) { 199 throw new IllegalArgumentException("Attempting to unregister the wrong listener"); 200 } 201 mListener = null; 202 } 203 204 /** 205 * Registers a listener that will receive callbacks when a load is canceled. 206 * The callback will be called on the process's main thread so it's safe to 207 * pass the results to widgets. 208 * 209 * Must be called from the process's main thread. 210 * 211 * @param listener The listener to register. 212 */ 213 public void registerOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { 214 if (mOnLoadCanceledListener != null) { 215 throw new IllegalStateException("There is already a listener registered"); 216 } 217 mOnLoadCanceledListener = listener; 218 } 219 220 /** 221 * Unregisters a listener that was previously added with 222 * {@link #registerOnLoadCanceledListener}. 223 * 224 * Must be called from the process's main thread. 225 * 226 * @param listener The listener to unregister. 227 */ 228 public void unregisterOnLoadCanceledListener(OnLoadCanceledListener<D> listener) { 229 if (mOnLoadCanceledListener == null) { 230 throw new IllegalStateException("No listener register"); 231 } 232 if (mOnLoadCanceledListener != listener) { 233 throw new IllegalArgumentException("Attempting to unregister the wrong listener"); 234 } 235 mOnLoadCanceledListener = null; 236 } 237 238 /** 239 * Return whether this load has been started. That is, its {@link #startLoading()} 240 * has been called and no calls to {@link #stopLoading()} or 241 * {@link #reset()} have yet been made. 242 */ 243 public boolean isStarted() { 244 return mStarted; 245 } 246 247 /** 248 * Return whether this loader has been abandoned. In this state, the 249 * loader <em>must not</em> report any new data, and <em>must</em> keep 250 * its last reported data valid until it is finally reset. 251 */ 252 public boolean isAbandoned() { 253 return mAbandoned; 254 } 255 256 /** 257 * Return whether this load has been reset. That is, either the loader 258 * has not yet been started for the first time, or its {@link #reset()} 259 * has been called. 260 */ 261 public boolean isReset() { 262 return mReset; 263 } 264 265 /** 266 * This function will normally be called for you automatically by 267 * {@link android.app.LoaderManager} when the associated fragment/activity 268 * is being started. When using a Loader with {@link android.app.LoaderManager}, 269 * you <em>must not</em> call this method yourself, or you will conflict 270 * with its management of the Loader. 271 * 272 * Starts an asynchronous load of the Loader's data. When the result 273 * is ready the callbacks will be called on the process's main thread. 274 * If a previous load has been completed and is still valid 275 * the result may be passed to the callbacks immediately. 276 * The loader will monitor the source of 277 * the data set and may deliver future callbacks if the source changes. 278 * Calling {@link #stopLoading} will stop the delivery of callbacks. 279 * 280 * <p>This updates the Loader's internal state so that 281 * {@link #isStarted()} and {@link #isReset()} will return the correct 282 * values, and then calls the implementation's {@link #onStartLoading()}. 283 * 284 * <p>Must be called from the process's main thread. 285 */ 286 public final void startLoading() { 287 mStarted = true; 288 mReset = false; 289 mAbandoned = false; 290 onStartLoading(); 291 } 292 293 /** 294 * Subclasses must implement this to take care of loading their data, 295 * as per {@link #startLoading()}. This is not called by clients directly, 296 * but as a result of a call to {@link #startLoading()}. 297 */ 298 protected void onStartLoading() { 299 } 300 301 /** 302 * Attempt to cancel the current load task. 303 * Must be called on the main thread of the process. 304 * 305 * <p>Cancellation is not an immediate operation, since the load is performed 306 * in a background thread. If there is currently a load in progress, this 307 * method requests that the load be canceled, and notes this is the case; 308 * once the background thread has completed its work its remaining state 309 * will be cleared. If another load request comes in during this time, 310 * it will be held until the canceled load is complete. 311 * 312 * @return Returns <tt>false</tt> if the task could not be canceled, 313 * typically because it has already completed normally, or 314 * because {@link #startLoading()} hasn't been called; returns 315 * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task 316 * is still running and the {@link OnLoadCanceledListener} will be called 317 * when the task completes. 318 */ 319 public boolean cancelLoad() { 320 return onCancelLoad(); 321 } 322 323 /** 324 * Subclasses must implement this to take care of requests to {@link #cancelLoad()}. 325 * This will always be called from the process's main thread. 326 * 327 * @return Returns <tt>false</tt> if the task could not be canceled, 328 * typically because it has already completed normally, or 329 * because {@link #startLoading()} hasn't been called; returns 330 * <tt>true</tt> otherwise. When <tt>true</tt> is returned, the task 331 * is still running and the {@link OnLoadCanceledListener} will be called 332 * when the task completes. 333 */ 334 protected boolean onCancelLoad() { 335 return false; 336 } 337 338 /** 339 * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously 340 * loaded data set and load a new one. This simply calls through to the 341 * implementation's {@link #onForceLoad()}. You generally should only call this 342 * when the loader is started -- that is, {@link #isStarted()} returns true. 343 * 344 * <p>Must be called from the process's main thread. 345 */ 346 public void forceLoad() { 347 onForceLoad(); 348 } 349 350 /** 351 * Subclasses must implement this to take care of requests to {@link #forceLoad()}. 352 * This will always be called from the process's main thread. 353 */ 354 protected void onForceLoad() { 355 } 356 357 /** 358 * This function will normally be called for you automatically by 359 * {@link android.app.LoaderManager} when the associated fragment/activity 360 * is being stopped. When using a Loader with {@link android.app.LoaderManager}, 361 * you <em>must not</em> call this method yourself, or you will conflict 362 * with its management of the Loader. 363 * 364 * <p>Stops delivery of updates until the next time {@link #startLoading()} is called. 365 * Implementations should <em>not</em> invalidate their data at this point -- 366 * clients are still free to use the last data the loader reported. They will, 367 * however, typically stop reporting new data if the data changes; they can 368 * still monitor for changes, but must not report them to the client until and 369 * if {@link #startLoading()} is later called. 370 * 371 * <p>This updates the Loader's internal state so that 372 * {@link #isStarted()} will return the correct 373 * value, and then calls the implementation's {@link #onStopLoading()}. 374 * 375 * <p>Must be called from the process's main thread. 376 */ 377 public void stopLoading() { 378 mStarted = false; 379 onStopLoading(); 380 } 381 382 /** 383 * Subclasses must implement this to take care of stopping their loader, 384 * as per {@link #stopLoading()}. This is not called by clients directly, 385 * but as a result of a call to {@link #stopLoading()}. 386 * This will always be called from the process's main thread. 387 */ 388 protected void onStopLoading() { 389 } 390 391 /** 392 * This function will normally be called for you automatically by 393 * {@link android.app.LoaderManager} when restarting a Loader. When using 394 * a Loader with {@link android.app.LoaderManager}, 395 * you <em>must not</em> call this method yourself, or you will conflict 396 * with its management of the Loader. 397 * 398 * Tell the Loader that it is being abandoned. This is called prior 399 * to {@link #reset} to have it retain its current data but not report 400 * any new data. 401 */ 402 public void abandon() { 403 mAbandoned = true; 404 onAbandon(); 405 } 406 407 /** 408 * Subclasses implement this to take care of being abandoned. This is 409 * an optional intermediate state prior to {@link #onReset()} -- it means that 410 * the client is no longer interested in any new data from the loader, 411 * so the loader must not report any further updates. However, the 412 * loader <em>must</em> keep its last reported data valid until the final 413 * {@link #onReset()} happens. You can retrieve the current abandoned 414 * state with {@link #isAbandoned}. 415 */ 416 protected void onAbandon() { 417 } 418 419 /** 420 * This function will normally be called for you automatically by 421 * {@link android.app.LoaderManager} when destroying a Loader. When using 422 * a Loader with {@link android.app.LoaderManager}, 423 * you <em>must not</em> call this method yourself, or you will conflict 424 * with its management of the Loader. 425 * 426 * Resets the state of the Loader. The Loader should at this point free 427 * all of its resources, since it may never be called again; however, its 428 * {@link #startLoading()} may later be called at which point it must be 429 * able to start running again. 430 * 431 * <p>This updates the Loader's internal state so that 432 * {@link #isStarted()} and {@link #isReset()} will return the correct 433 * values, and then calls the implementation's {@link #onReset()}. 434 * 435 * <p>Must be called from the process's main thread. 436 */ 437 public void reset() { 438 onReset(); 439 mReset = true; 440 mStarted = false; 441 mAbandoned = false; 442 mContentChanged = false; 443 mProcessingChange = false; 444 } 445 446 /** 447 * Subclasses must implement this to take care of resetting their loader, 448 * as per {@link #reset()}. This is not called by clients directly, 449 * but as a result of a call to {@link #reset()}. 450 * This will always be called from the process's main thread. 451 */ 452 protected void onReset() { 453 } 454 455 /** 456 * Take the current flag indicating whether the loader's content had 457 * changed while it was stopped. If it had, true is returned and the 458 * flag is cleared. 459 */ 460 public boolean takeContentChanged() { 461 boolean res = mContentChanged; 462 mContentChanged = false; 463 mProcessingChange |= res; 464 return res; 465 } 466 467 /** 468 * Commit that you have actually fully processed a content change that 469 * was returned by {@link #takeContentChanged}. This is for use with 470 * {@link #rollbackContentChanged()} to handle situations where a load 471 * is cancelled. Call this when you have completely processed a load 472 * without it being cancelled. 473 */ 474 public void commitContentChanged() { 475 mProcessingChange = false; 476 } 477 478 /** 479 * Report that you have abandoned the processing of a content change that 480 * was returned by {@link #takeContentChanged()} and would like to rollback 481 * to the state where there is again a pending content change. This is 482 * to handle the case where a data load due to a content change has been 483 * canceled before its data was delivered back to the loader. 484 */ 485 public void rollbackContentChanged() { 486 if (mProcessingChange) { 487 onContentChanged(); 488 } 489 } 490 491 /** 492 * Called when {@link ForceLoadContentObserver} detects a change. The 493 * default implementation checks to see if the loader is currently started; 494 * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag 495 * so that {@link #takeContentChanged()} returns true. 496 * 497 * <p>Must be called from the process's main thread. 498 */ 499 public void onContentChanged() { 500 if (mStarted) { 501 forceLoad(); 502 } else { 503 // This loader has been stopped, so we don't want to load 504 // new data right now... but keep track of it changing to 505 // refresh later if we start again. 506 mContentChanged = true; 507 } 508 } 509 510 /** 511 * For debugging, converts an instance of the Loader's data class to 512 * a string that can be printed. Must handle a null data. 513 */ 514 public String dataToString(D data) { 515 StringBuilder sb = new StringBuilder(64); 516 DebugUtils.buildShortClassTag(data, sb); 517 sb.append("}"); 518 return sb.toString(); 519 } 520 521 @Override 522 public String toString() { 523 StringBuilder sb = new StringBuilder(64); 524 DebugUtils.buildShortClassTag(this, sb); 525 sb.append(" id="); 526 sb.append(mId); 527 sb.append("}"); 528 return sb.toString(); 529 } 530 531 /** 532 * Print the Loader's state into the given stream. 533 * 534 * @param prefix Text to print at the front of each line. 535 * @param fd The raw file descriptor that the dump is being sent to. 536 * @param writer A PrintWriter to which the dump is to be set. 537 * @param args Additional arguments to the dump request. 538 */ 539 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 540 writer.print(prefix); writer.print("mId="); writer.print(mId); 541 writer.print(" mListener="); writer.println(mListener); 542 if (mStarted || mContentChanged || mProcessingChange) { 543 writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); 544 writer.print(" mContentChanged="); writer.print(mContentChanged); 545 writer.print(" mProcessingChange="); writer.println(mProcessingChange); 546 } 547 if (mAbandoned || mReset) { 548 writer.print(prefix); writer.print("mAbandoned="); writer.print(mAbandoned); 549 writer.print(" mReset="); writer.println(mReset); 550 } 551 } 552 }