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