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