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