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