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