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