zoukankan      html  css  js  c++  java
  • LoaderManager使用详解(三)---实现Loaders

    这篇文字将介绍Loader<D>类,并且介绍自定义Loader的实现。这是本系列的第三篇文章。

    三:实现Loaders

    重中之重,如果你还没有读过前面两篇文章,我建议你在深入之前先读一读那两篇文章。先简短的 总结一下这篇博客覆盖了什么内容。Loader之前的世界(第一篇)描述了Android3.0之前的数据载入方法和在UI主线程中执行的冗长的查询操 作。这些UI非友好的API导致了应用响应变差。总总情况就是了解LoaderManager(第二篇)的写作动机。这篇文章介绍了 LoaderManager类,并且讲到了它在异步载入数据中所扮演的角色。LoaderManager在Activity和Fragment的声明周期 中管理Loaders,并且在配置变化时保持已载入的数据(译者注:避免了Activity重启导致数据重载入)。

    Loader基础


    Loades负责在一个单独线程中执行查询,监控数据源改变,当探测到改变时将查询到的结果集发送到注册的监听器上(通常是LoaderManager)。下面这些特性使Loaders成为AndroidSDK中强大的工具:

    1. 它封装了实际的数据载入。Activity/Fragment不再需要知道如何载入数据。实际上,Activity/Fragment将该任务委托给了Loader,它在后台执行查询要求并且将结果返回给Activity/Fragment。

    2. 客户端不需要知道查询如何执行。Activity/Fragment不需要担心查询如何在独立的线程中执行,Loder会自动执行这些查询操作。这种方式不仅减少了代码复杂度同事也消除了线程相关bug的潜在可能。

    3. 它是为安全的事件驱动方式。Loader检测底层数据,当检测到改变时,自动执行新的载入获取最新数据。这使得使用Loader变得容易,客户端可以相信 Loader将会自己自动更新它的数据。Activity/Fragment所需要做的就是初始化Loader,并且对任何反馈回来的数据进行响应。除此 之外,所有其他的事情都由Loader来解决。

    Loaders是一个比较高级的话题,可能需要更多时间来使用它。在下一节中,我们会从分析它的四个定义的特性来开始。

    Loader由什么组成?


    总共有四个特性最终决定了一个Loader的行为:

    1. 执行异步载入的任务。为了确保在一个独立线程中执行载入操作,Loader的子类必须继承AsyncTaskLoader<D>而不是Loader<D>类。AsyncTaskLoader<D>是一个抽象Loader,它提供了一个AsyncTask来做它的执行操作。当定义子类时,通过实现抽象方法loadInBackground方法来实现异步task。该方法将在一个工作线程中执行数据加载操作。

    2. 在一个注册监听器中接收载入完成返回的结果(见附注1)。对于每个Loader来说,LoaderManager注册一个 OnLoadCompleteListener<D>,该对象将通过调用onLoadFinished(Loader<D> loader, D result)方法使Loader将结果传送给客户端。Loader通过调用Loader#deliverResult(D result),将结果传递给已注册的监听器们。

    3. 三种不同状态(见附注2)。任何Loader将处于三种状态之中,已启动、已停止、重启:
    a. 处于已启动状态的Loader会执行载入操作,并在任何时间将结果传递到监听器中。已启动的Loader将会监听数据改变,当检测到改变时执行新的载入。 一旦启动,Loader将一直处在已启动状态,一直到转换到已停止和重启。这是唯一一种onLoadFinished永远不会调用的状态。
    b. 处于已停止状态的Loader将会继续监听数据改变,但是不会将结果返回给客户端。在已停止状态,Loader可能被启动或者重启。
    c. 当Loader处于重启状态时,将不会执行新的载入操作,也不会发送新的结果集,也不会检测数据变化。当一个Loader进入重启状态,它必须解除对应的 数据引用,方便垃圾回收(同样地,客户端必须确定,在Loader无效之后,移除了所有该数据的引用)。通常,重启Loader不会两次调用;然而,在某 些情况下他们可能会启动,所以如果必要的话,它们必须能够适时重启。

    4. 有一个观察者接受数据源改变的通知。Loader必须实现这些Observer其中之一(比如 ContentObserver,BroadcastReceiver等),来检测底层数据源的改变。当检测到数据改变,观察者必须调用 Loader#onContentChanged()。在该方法中执行两种不同操作:a. 如果Loader已经处于启动状态,就会执行一个新的载入操作; b. 设置一个flag标识数据源有改变,这样当Loader再次启动时,就知道应该重新载入数据了。

    到目前为止,你应该基本知道了Loader如何工作了。如果没有的话,我建议你先放一放,稍后再重新读一遍(读一下这篇文档,)。也就是说,让我们从实际代码入手,写写看。

    实现Loader


    就如我之前陈述的那样,在实现自定义Loader的时候有很多需要注意。子类必须实现 loadInBackground()方法,必须覆写onStartLoading(), onStoppLoading(),onReset(),onCanceled()和deliverResult(D results)来实现一个完整功能的Loader。覆写这些方法非常重要,LoaderManager将会在Activity/Fragment不同声 明周期调用不同的方法。例如,当一个Activity第一次启动,它将会让LoaderManager在Activity#onStart()中启动它所 拥有的每个Loaders。如果一个Loader没有启动,LoaderManager将会调用startLoading()方法,该方法将Loader 进入已启动状态并且立即调用Loader的onStartLoading()方法。也就是说,LoaderManager在后台所做的大量工作都是基于 Loader正确实现的基础上,所以不要小看实现这些方法的重要性。

    下面的代码就是Loader典型实现的样板。SampleLoader查询结果为一个包含SampleItem对象的列表,并且将查询结果列表List<SampleItem>返回给客户端:

    1. public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {  
    2.   
    3.   // We hold a reference to the Loader’s data here.  
    4.   private List<SampleItem> mData;  
    5.   
    6.   public SampleLoader(Context ctx) {  
    7.     // Loaders may be used across multiple Activitys (assuming they aren't  
    8.     // bound to the LoaderManager), so NEVER hold a reference to the context  
    9.     // directly. Doing so will cause you to leak an entire Activity's context.  
    10.     // The superclass constructor will store a reference to the Application  
    11.     // Context instead, and can be retrieved with a call to getContext().  
    12.     super(ctx);  
    13.   }  
    14.   
    15.   /****************************************************/  
    16.   /** (1) A task that performs the asynchronous load **/  
    17.   /****************************************************/  
    18.   
    19.   @Override  
    20.   public List<SampleItem> loadInBackground() {  
    21.     // This method is called on a background thread and should generate a  
    22.     // new set of data to be delivered back to the client.  
    23.     List<SampleItem> data = new ArrayList<SampleItem>();  
    24.   
    25.     // TODO: Perform the query here and add the results to 'data'.  
    26.   
    27.     return data;  
    28.   }  
    29.   
    30.   /********************************************************/  
    31.   /** (2) Deliver the results to the registered listener **/  
    32.   /********************************************************/  
    33.   
    34.   @Override  
    35.   public void deliverResult(List<SampleItem> data) {  
    36.     if (isReset()) {  
    37.       // The Loader has been reset; ignore the result and invalidate the data.  
    38.       releaseResources(data);  
    39.       return;  
    40.     }  
    41.   
    42.     // Hold a reference to the old data so it doesn't get garbage collected.  
    43.     // We must protect it until the new data has been delivered.  
    44.     List<SampleItem> oldData = mData;  
    45.     mData = data;  
    46.   
    47.     if (isStarted()) {  
    48.       // If the Loader is in a started state, deliver the results to the  
    49.       // client. The superclass method does this for us.  
    50.       super.deliverResult(data);  
    51.     }  
    52.   
    53.     // Invalidate the old data as we don't need it any more.  
    54.     if (oldData != null && oldData != data) {  
    55.       releaseResources(oldData);  
    56.     }  
    57.   }  
    58.   
    59.   /*********************************************************/  
    60.   /** (3) Implement the Loader’s state-dependent behavior **/  
    61.   /*********************************************************/  
    62.   
    63.   @Override  
    64.   protected void onStartLoading() {  
    65.     if (mData != null) {  
    66.       // Deliver any previously loaded data immediately.  
    67.       deliverResult(mData);  
    68.     }  
    69.   
    70.     // Begin monitoring the underlying data source.  
    71.     if (mObserver == null) {  
    72.       mObserver = new SampleObserver();  
    73.       // TODO: register the observer  
    74.     }  
    75.   
    76.     if (takeContentChanged() || mData == null) {  
    77.       // When the observer detects a change, it should call onContentChanged()  
    78.       // on the Loader, which will cause the next call to takeContentChanged()  
    79.       // to return true. If this is ever the case (or if the current data is  
    80.       // null), we force a new load.  
    81.       forceLoad();  
    82.     }  
    83.   }  
    84.   
    85.   @Override  
    86.   protected void onStopLoading() {  
    87.     // The Loader is in a stopped state, so we should attempt to cancel the   
    88.     // current load (if there is one).  
    89.     cancelLoad();  
    90.   
    91.     // Note that we leave the observer as is. Loaders in a stopped state  
    92.     // should still monitor the data source for changes so that the Loader  
    93.     // will know to force a new load if it is ever started again.  
    94.   }  
    95.   
    96.   @Override  
    97.   protected void onReset() {  
    98.     // Ensure the loader has been stopped.  
    99.     onStopLoading();  
    100.   
    101.     // At this point we can release the resources associated with 'mData'.  
    102.     if (mData != null) {  
    103.       releaseResources(mData);  
    104.       mData = null;  
    105.     }  
    106.   
    107.     // The Loader is being reset, so we should stop monitoring for changes.  
    108.     if (mObserver != null) {  
    109.       // TODO: unregister the observer  
    110.       mObserver = null;  
    111.     }  
    112.   }  
    113.   
    114.   @Override  
    115.   public void onCanceled(List<SampleItem> data) {  
    116.     // Attempt to cancel the current asynchronous load.  
    117.     super.onCanceled(data);  
    118.   
    119.     // The load has been canceled, so we should release the resources  
    120.     // associated with 'data'.  
    121.     releaseResources(data);  
    122.   }  
    123.   
    124.   private void releaseResources(List<SampleItem> data) {  
    125.     // For a simple List, there is nothing to do. For something like a Cursor, we   
    126.     // would close it in this method. All resources associated with the Loader  
    127.     // should be released here.  
    128.   }  
    129.   
    130.   /*********************************************************************/  
    131.   /** (4) Observer which receives notifications when the data changes **/  
    132.   /*********************************************************************/  
    133.    
    134.   // NOTE: Implementing an observer is outside the scope of this post (this example  
    135.   // uses a made-up "SampleObserver" to illustrate when/where the observer should   
    136.   // be initialized).   
    137.     
    138.   // The observer could be anything so long as it is able to detect content changes  
    139.   // and report them to the loader with a call to onContentChanged(). For example,  
    140.   // if you were writing a Loader which loads a list of all installed applications  
    141.   // on the device, the observer could be a BroadcastReceiver that listens for the  
    142.   // ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular   
    143.   // Loader whenever the receiver detects that a new application has been installed.  
    144.   // Please don’t hesitate to leave a comment if you still find this confusing! :)  
    145.   private SampleObserver mObserver;  
    146. }  

    总结


    我希望本文对你有用,并且通过它可以很好的理解Loaders和LoaderManager 如何协同工作来执行异步任务,自动更新查询结果。记住,Loader是你的朋友。。。如果你使用它们,你的app将从相应性能、所需代码量中收益。我希望 通过把它们的细节列举出来,可以减小它的学习曲线。

    附注

    1. 你不需要担心为你的Loader注册监听器,除非你不准备跟LoaderManager协同使用。LoaderManager担任的就是 “listener”的角色,并将Loader返回的任何结果传给LoaderCallbacks#LoadFinished方法。
    2. Loader也有可能处于“abandoned”状态(译者注:丢弃状态?)。这个是一个可选的中间状态,处于停止状态和重置状态之间。为了更简明的理解,再这里不讨论丢弃状态。也就是说,以我的经验来看,通常并无必要实现onAbandon()方法。
  • 相关阅读:
    HDU 5883 F
    关于codeblock 为什么不能调试
    Codeforces Round #378 (Div. 2) D. Kostya the Sculptor 分组 + 贪心
    51NOD 区间的价值 V2
    NYOJ 42 一笔画问题
    如何对二维字符数组进行排序
    hihoCoder 1383 : The Book List 北京网络赛
    利用IDA学习一个简单的安卓脱壳
    iOS APP可执行文件的组成
    Mac10.11 搭建php开发环境
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4378909.html
Copyright © 2011-2022 走看看