这篇文字将介绍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. ssh环境搭建并实现登录功能

    参照了这篇博客,但是里面有些地方进行了更改 http://wenku.baidu.com/link?url=edeegTquV2eR3CJ5-zvNcJbyuq11Afp-lD2Fz2jfmuHhV1 ...

  2. iOS实践03

    主要目标:版本新特性界面,新浪授权界面(登录界面)的处理 任务基本完成了,基本的框架也就到这了,接下来的应该是首页获取微博了. 1.版本新特性,可以单独作为一个model,写完之加入到项目中.我们新建 ...

  3. 模拟JQUERY的延迟方法绑定

    模拟JQUERY的延迟方法绑定, 对于延迟方法绑定,各种语言都有不同的描述 什么回调啊,函数指针啊,委托啊,事件啊等,其实也就是那么大回事,不过用好这些特性,对于扩展和架构是非常有好处的, 好处自然就 ...

  4. ssma for oracle

    SQL Server Migration Assistant (SSMA) for Oracle lets you quickly convert Oracle database schemas to ...

  5. Push segues can only be used when the.....

    刚刚遇到的两个错误,. 1, Terminating app due to uncaught exception'NSGenericException', reason: 'Push segues c ...

  6. 网页中获取网络mp3文件的时常

    <html> <audio id="audio" controls> <source src="http://cdn.kaishuhezi. ...

  7. 从事web前端两年半后的迷茫

    做了两年半的重构,突然有种迷茫的感觉,好像瓶颈了,不知道自己该怎么继续走下去,以前刚毕业的时候,总觉得自己有好多的东西要学在前端方面,所以有那个促使自己去学习的动力,每当没工作任务的时候,自己总是去主 ...

  8. perl lwp 超时问题

    lwp 超时问题: jrhmpt01:/root/async# cat a1.pl use LWP::UserAgent; use utf8; use DBI; use POSIX; use Data ...

  9. HDU 1130 How Many Trees?

    裸的卡特兰数 C++#include<iostream> #include<cstdio> using namespace std; #define base 10000 #d ...

  10. (译)Node.js的全局变量

    原文标题:Global Variables in Node.js 原文链接:http://www.hacksparrow.com/global-variables-in-node-js.html 你可 ...