这篇文字将介绍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. client不须要知道查询怎样运行。Activity/Fragment不须要操心查询怎样在独立的线程中运行,Loder会自己主动运行这些查询操作。这样的方式不仅降低了代码复杂度同事也消除了线程相关bug的潜在可能。

3. 它是为安全的事件驱动方式。Loader检測底层数据,当检測到改变时,自己主动运行新的加载获取最新数据。这使得使用Loader变得easy,client能够相信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将结果传送给client。Loader通过调用Loader#deliverResult(D result),将结果传递给已注冊的监听器们。

3. 三种不同状态(见附注2)。不论什么Loader将处于三种状态之中,已启动、已停止、重新启动:
a. 处于已启动状态的Loader会运行加载操作,并在不论什么时间将结果传递到监听器中。已启动的Loader将会监听数据改变,当检測到改变时运行新的加载。一旦启动,Loader将一直处在已启动状态,一直到转换到已停止和重新启动。这是唯一一种onLoadFinished永远不会调用的状态。
b. 处于已停止状态的Loader将会继续监听数据改变,可是不会将结果返回给client。在已停止状态,Loader可能被启动或者重新启动。
c. 当Loader处于重新启动状态时,将不会运行新的加载操作,也不会发送新的结果集,也不会检測数据变化。当一个Loader进入重新启动状态,它必须解除相应的数据引用,方便垃圾回收(相同地,client必须确定,在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>返回给client:

public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {

  // We hold a reference to the Loader’s data here.
private List<SampleItem> mData; public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
} /****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/ @Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>(); // TODO: Perform the query here and add the results to 'data'. return data;
} /********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/ @Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
} // Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data; if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
} // Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
} /*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/ @Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
} // Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
} if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
} @Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad(); // Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
} @Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading(); // At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
} // The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
} @Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data); // The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
} private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
} /*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/ // NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized). // The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}

总结


我希望本文对你实用,而且通过它能够非常好的理解Loaders和LoaderManager怎样协同工作来运行异步任务,自己主动更新查询结果。记住,Loader是你的朋友。。。假设你使用它们,你的app将从对应性能、所需代码量中收益。我希望通过把它们的细节列举出来,能够减小它的学习曲线。

附注

1. 你不须要操心为你的Loader注冊监听器,除非你不准备跟LoaderManager协同使用。LoaderManager担任的就是“listener”的角色,并将Loader返回的不论什么结果传给LoaderCallbacks#LoadFinished方法。
2. Loader也有可能处于“abandoned”状态(译者注:丢弃状态?)。这个是一个可选的中间状态,处于停止状态和重置状态之间。为了更简明的理解,再这里不讨论丢弃状态。也就是说,以我的经验来看,通常并无必要实现onAbandon()方法。

LoaderManager使用具体解释(三)---实现Loaders的更多相关文章

  1. LoaderManager使用具体解释(二)---了解LoaderManager

    了解LoaderManager 这篇文章将介绍LoaderManager类,这是该系列的第二篇文章. 一:Loaders之前世界 二:了解LoaderManager 三:实现Loaders 四:实例: ...

  2. LoaderManager使用具体解释(四)---实例:AppListLoader

    实例:AppListLoader 这篇文章将是我的第四篇,也就是最后一篇该系列的文章.请在评论里面告诉我他们是否实用.前面几篇文章的链接例如以下: 一:Loaders之前世界 二:了解LoaderMa ...

  3. LoaderManager使用具体解释(一)---没有Loader之前的世界

    来源: http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html 感谢作者Alex ...

  4. TCP/IP具体解释--三次握手和四次握手 Dos攻击

    TCP连接的状态图 TCP建立连接的三次握手过程,以及关闭连接的四次握手过程 贴一个telnet建立连接,断开连接的使用wireshark捕获的packet截图. 1.建立连接协议(三次握手) (1) ...

  5. Netty4具体解释三:Netty架构设计

         读完这一章,我们基本上能够了解到Netty全部重要的组件,对Netty有一个全面的认识.这对下一步深入学习Netty是十分重要的,而学完这一章.我们事实上已经能够用Netty解决一些常规的问 ...

  6. Android WebView 开发具体解释(三)

    转载请注明出处   http://blog.csdn.net/typename/article/details/40302351 powered by miechal zhao 概览 Android ...

  7. kafka解释三的具体:发展Kafka应用

    一个.整体外观Kafka 我们知道.Kafka系统有三大组件:Producer.Consumer.broker . producers 生产(produce)消息(message)并推(push)送给 ...

  8. 【原】webpack--loaders,主要解释为什么需要loaders和注意事项

    Why需要loaders? webpack开箱即用只支持JS和JSON两种文件类型,但是比如css.less,还有目前市场上比较新的语法糖jsx,怎么处理呢? 通过Loaders去支持其他文件类型并且 ...

  9. Android清单文件具体解释(三)----应用程序的根节点&lt;application&gt;

    <application>节点是AndroidManifest.xml文件里必须持有的一个节点,它包括在<manifest>节点下.通过<application>节 ...

随机推荐

  1. eclipse修改编译路径

    右击项目--properties--java build path--点击source,修改最下方的路径即可

  2. 深刻理解void,void*和sizeof关键字

    void的字面值是“无类型”,void*则是"无类型指针".void*可以指向任何类型的数据.void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个 ...

  3. python质量控制

    一种编写高质量软件的方式是给代码中每个函数写测试,在开发过程中经常性的进行测试.         doctest模块可以在docstring中嵌套测试代码.例如: def average(values ...

  4. IOS本地化。

    1,项目名本地化 点击项目,蓝色图标->info 最下面+号,添加chinese本地化. Supporting Files->infoPlist.strings 下会有两个文件,有一个是设 ...

  5. mybatis+spring+c3p0+maven+ehcache

    项目截图 pom.xml如下 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http: ...

  6. php文件链接数据库基本代码

    <?php $conn=@mysql_connect("localhost","root",""); if($conn==null) ...

  7. github 的分支操作

    首先需要当前目录设置为仓库目录 一.创建本地分支 1.查看有哪些分支:git branch 2.创建一个分支:git branch name  ,其中name是分支名 3.切换到分支:git chec ...

  8. Android清单文件具体解释(三)----应用程序的根节点&lt;application&gt;

    <application>节点是AndroidManifest.xml文件里必须持有的一个节点,它包括在<manifest>节点下.通过<application>节 ...

  9. map的erase()释放内存

    STL中的map调用erase(it),当value值为指针时,释放内存: #include <iostream> #include <map> #include <st ...

  10. ubuntu14.04LTS ruby on rails 开发环境

    小弟初学 Ruby,也没用过Linux. 在网上搜了好多关于开发环境的配置的文章,但总是和实际有点出入,找了N遍文章后,终于找到最简环境安装配置方法,分享下 推荐用 Ubuntu,感觉对于习惯用Win ...