Loader框架

在开发APP的时候,都涉及到将后台数据更新反馈到UI上面。

第一种方式:传统的观察者模式

通过数据访问层来将数据load到UI层并初始化UI. (数据层可理解为提供数据的那一部分:可以是来自于网络,也可来自于本地内存或数据库)
然后让数据层和UI层利用观察者模式衔接起来,当数据层有数据变化的时候,利用UI注册过来的监听者来异步的通知UI去更新。
这种方式使得UI和数据层紧密的关联在一起。数据层要继承Observable ,而UI则需要实现Observer接口. 当UI被销毁前需要注意注销对数据层的关注,.这种方式用起来比较麻烦,UI和数据层有较多的耦合。

第二种方式:使用Loader框架

官方的定义先抛一边。

Loader:

是一个抽象类,本身其提供了一系列的规范接口,在这些接口中需要去实现和具体数据层进行交互的工作,同步或者异步返回的数据

LoaderManager :

其通过Activity或者fragment的getLoaderManager获取得到,在其初始化方法initLoader中传入LoaderManager.LoaderCallbacks 回调对象。
其主要做2件事情:
1. 回调通知创建相应的Loader实例.
2. 回调通知有数据更新. 在该方法中实现相应的UI更新或数据处理。

与Fragment及Activity生命周期的关联

Fragment/Activity 生命周期LoaderLoaderManager.LoaderCallbacks
onStartstartLoadingonCreateLoader
onStopstopLoading()-
onDestroyresetonLoaderReset

注:当使用AsyncTaskLoader的时候,在页面切换使后,内部并不会调用Loader.cancelLoad()方法来退出当前执行的任务(只有restartLoader才会执行)。在CursorLoader中reset()方法可处理Cursor的关闭

综合以上,Loader框架将UI彻底和数据层隔离耦合。对于UI来说,他不需要去了解数据访问的细节问题,Loader将和数据层的交互工作封装起来,可方便复用。另外因为Loader还和UI的生命周期结合起来,比如,在UI即将结束的时候,自动的进行一些资源回收的工作。

Loader的两个子类

Loader定义了一些规范,很多方法都是空的。我们更多的是使用其子类。

AsyncTaskLoader:

AsyncTaskLoader内部增加了一个LoadTask内部类,其继承自AsyncTask(v4包中为ModernAsyncTask)以及实现Runnable接口,当我们的数据层发生异步变化时候,会去触发Loader的onContentChanged()方法,该方法的驱动下会实例化一个LoadTask并回调执行onLoadInBackground。该task会在初始化AsyncTaskLoader时候传入的线程池里执行(这里其使用的是LoadTask所继承的AsyncTask特性来执行,那么其继承Runnable接口的作用在后面讲解.Mark:A1)。

setUpdateThrottle设置更新节流阀

这里有个setUpdateThrottle方法,之前一直不了解其作用,这在某种场景下使用:比如数据层变化十分频繁,如果不设置则会导致UI层频繁重绘,这带来严重的性能问题。

public void setUpdateThrottle(long delayMS) {
        mUpdateThrottle = delayMS;
        if (delayMS != 0) {
            mHandler = new Handler();
        }
    }

其意义在于 从上次执行LoadInBackground开始后的delayMS 时间里不要急着再去载入数据。
在executePendingTask方法中:

if (mUpdateThrottle > 0) {
                long now = SystemClock.uptimeMillis();
                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
                    // Not yet time to do another load.
                    if (DEBUG) Log.v(TAG, "Waiting until "
                            + (mLastLoadCompleteTime+mUpdateThrottle)
                            + " to execute: " + mTask);
                    mTask.waiting = true;
                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
                    return;
                }
            }

可以看出来,当在上次执行LoadInBackground开始后的delayMS 时间里重新载入数据请求来,其利用Handler.postAtTime的方式来延时执行任务,这里其使用的是LoadTask所实现的Runnable接口特性(见Mark:A1)

* 那么该Handler在哪个线程上运行呢?

setUpdateThrottle 方法执行的时候,调用 mHandler = new Handler()就是默认使用调用线程的looper,如果是主线程,则会使用主线程的looper.
问题来了?

* 这个不会阻塞主线程的风险吗?

没有,因为其实在实际执行的时候,最终调用的还是executePendingTask

@Override
        public void run() {
            waiting = false;
            AsyncTaskLoader.this.executePendingTask();
        }

那么耗时的事情最终还是交给了AsyncTask来处理了

* 如果在普通线程中调用setUpdateThrottle会有什么问题?

问题是有的,如果该线程没有相关联的looper(也就是没有Looper.prepare())的话,就可能出现crash问题。
参看官方

Note on threading: Clients of loaders should as a rule perform any calls on to a Loader from the main thread of their process (that is, the thread the Activity callbacks and other things occur on). Subclasses of Loader (such as AsyncTaskLoader) will often perform their work in a separate thread, but when delivering their results this too should be done on the main thread.

要求在主线程上调用Loader方法

项目相关(非Android系统)

ConcurrentTaskLoader:

因为直接使用AsyncTaskLoader需要提供线程池,那么为了省事,线程池使用ModernAsyncTask提供的线程池THREAD_POOL_EXECUTOR。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值