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