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