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