Android Loader LoaderManager 总结(一)

前言:

        本篇文章是对Loader 和 LoaderManager使用和梳理总结


Loader简介:

        Android装载器Loader是从Android 3 引入的 ,使用相关API 可以从数据库,网络,内容管理

者等数据源中加载数据,然后显示在 Fragment 或 Activity上,模型图如下:

                        

        不过从Android P (API 28) 起,加载器已被弃用。在处理Activity Fragment 生命周期中加载数据,官方推荐选项用ViewModels + LiveData的组合, 本篇文章还是重点讲解Loader,LoaderManager的使用。

        常见的异步任务有如下几种方式:

        1. Thread + Handler 

        2. AysncTask

        3. IntentServcie            

        4. Loader

        那Google为什么要推荐使用Loader 呢? 解释如下:

        1. 如果你直接在Activity 或 Fragment 获取数据,造成用户在UI 线程中执行可能耗时的查询而缺乏响应能力,也有可能引起ANR。

        2. 如果创建一个异步任务(比如AysncTask)来获取数据,一般场景可以满足要求,但是有个缺点:当组件Activity或Fragment因屏幕旋转等因素被销毁重建后,AysncTask又会重新执行务。

         你在组件生命周期方法onCreate()中开启一个异步任务(AsyncTask.excute(Params...params))方法,并在任务结束后(AsyncTask.onPostExecute())返回结果,然后在onResume()中更新UI。此时,旋转屏幕,生命周期如下图:

        

                第一种场景下,旋转屏幕,当前的Activity会被销毁然后重建,Activity重启后,此时的AsyncTask对Activity的引用还是之前没有转屏的,并不是重建之后的, onPostExecute对重建后的Activity是没有意义的,之前做的任务就白白浪费了。当然呢,也有解决办法,就是保留对AsyncTask的引用,该引用在配置更改之间持续,在重新启动时更新目标Activity。

               但是Loader就没有这个缺点,Activity配置发生变化(如横竖屏切换)时不用重复加载数据,Loader 还有其他优点如下:

        Loader在单独的线程上运行,以防止 UI 出现卡顿或无响应。

        Loader通过在事件发生时提供回调方法来简化线程管理。

        Loader在配置更改中持久保存和缓存结果,以防止重复查询。

        Loader可以实现一个观察者来监控底层数据源的变化。例如,CursorLoader自动注册一个ContentObserver以在数据更改时触发重新加载。

Loader API:

        LoaderManager

一个与FragmentActivity和Fragment相关的抽象类,管理一个或多个Loader实例。 每个activity或fragment中只有一个LoaderManager,但是一个LoaderManager能管理多个Loader。

Loader调用initLoader()或restartLoader()开始读取数据。系统自动确定具有相同ID的Loader是否存在,并将创建新的Loader或者重新使用现用的Loader。

        LoaderManager.LoaderCallbacks

这个接口包含回调Loader事件触发时回调的方法。接口定义三个回调方法:

        onCreateLoader(int,Bundle) - 当需要一个新Loader被创建时调用此方法。代码应该创建一个Loader对象并返回给系统。
        onLoadFinished(Loader<D>,D) - 当Loader加载数据完成后调用此方法。通常,应该展示数据给用户。
        onLoaderReset(Loader<D>) - 当一个已经创建的Loader被重置是(当你调用destroyLoader(int))或activity(或fragment)已经销毁时调用此方法。 代码应该移除任何对Loader的数据的引用。

        

        Loader

Loader执行加载数据。该类是一个抽象的,作为基础类服务与所有Loader。你可以直接使用子类Loeader或者使用内置子类之一来简单实现。

        AsyncTaskLoader - 一个抽象Loader,提供一个AsyncTask在单独的线程执行加载操作。
        Cursoroader - 一个AsyncTaskLoader用于异步加载来自ContentProvider的数据。它请求一个ContentResolver并返回一个Cursor。

应用中使用Loader

app使用Loader通常包括以下内容:

一个Activity或Fragment的一个实例LoaderManager。

一个CursorLoader通过ContentProvider加载数据。或者,你可以实现你的子类Loader或AsyncTaskLoader加载其他来源的数据。

一个LoaderManager.LoaderCallbacks的实现。在你创建一个新Loader和管理已经存在Loader引用的地方。

一个数据源,例如ContentProvider,当使用一个CursorLoader

开启Loader

一个Activity或Fragment只有一个LoaderManager管理一个或多个Loader实例。通常在Activity的onCreate()方法或者在fragment的onActivityCreateed()方法中初始化Loader,如下:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this);

initLoader()方法参数如下:

        标识Loader的唯一的ID,这里ID是0
        提供给Loader的可选参数,这里传null
        LoaderManager.LoaderCallbacks的实现对象,LoaderManager会调用报告给Loader事件
initLoader()调用确保Loader被初始化和活跃。他有两种可能:

        如果ID指定的Loader已经存在,则使用最后创建的Loader
如果ID指定的Loader不存在,initLoader()触发LoaderManager.LoaderCallbacks的onCreateLoader()方法。这是实现代码来实例化并返回Loader的地方。
在任何一种情况下,给的LoaderManager.LoaderCallbacks实现都与Loader相关联,在Loader状态改变时被调用。如果调用的时候调用者处于开始状态,请求已经存在的Loader并生成了它的数据,系统会立马调用onLoadFinished()方法(在iniLoader()之间),你必须准备好这些的发生。

        注意initLoader()方法返回的被创建Loader,但是你不需要捕获它的引用。Loadermanager自动管理Loader的生命周期。必要时LoaderManager开始和停止加载数据,可以维持Loader相关内容的状态。这意味着,你很少和Loader直接交互(尽管使用Loader方法来微调Loader的行为)。

重启Loader

        当你使用initLoader(),如上所示,如果ID指定的Loader已经存在,用这个。如果不是就创建一个。但是有时候你想丢弃老数据并重新开始。

        要丢弃你的老数据,你用restartLoader()。例如,当用得查询队列变更时,这个SearchView.OnQueryTextListenerde实现会重启Loader。Loader需要重启,以便接受搜索过滤器做一次新的查询:

public boolean onQueryTextChanged(String newText) {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    curFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getSupportLoaderManager().restartLoader(0, null, this);
    return true;
}


 

LoaderManager.LoaderCallbacks的使用

LoaderManager.LoaderCallbacks是一个回调接口,让客户端通过LoaderManager与之交互。

CursorLoader预计Loader在停止之后将保留其数据。允许应用保持activity或fragment的onStop()和onStart()方法相关的数据,当用户返回应用程序时他们不必等待数据重载。你可以再LoaderManager.LoaderCallbacks方法合适使用这些方法创建一个新的Loader,并告诉应用合适停止使用Loader的数据。

        onCreateLoader() - 实例化并Loader为给定的ID 返回一个新的。

        onLoadFinished() - 当以前创建的加载程序完成加载时调用。

        onLoaderReset() - 当以前创建的加载程序正在重置时调用,从而使其数据不可用。
        这些方法在下面的章节中有更详细的描述。

onCreateLoader

        当你试图访问一个Loader(例如通过initLoader())时,它会检查该ID指定的Loader是否存在。如果没有,则触发LoaderManager.LoaderCallbacks的onCreateLoader()方法。这是你创建一个新的Loader的地方。通常将是CursorLoader(),但你能实现Loader的子类。

        在这个例子中,onCreateLoader()回调方法创建一个CursorLoader。你必须用CursorLoader的构造方法创建CursorLoader,这需要完整地信息集来执行查询ContentProvider。具体来说,它需要:

  • uri - 内容的URI
  • projection - 要返回的列的列表。传递null将返回所有列,这是比较低效的。
  • selection - 过滤器,声明返回哪些行,格式化为SQL WHERE子句(不包括WHERE本身)。传递null将返回给定URI的所有行。
  • selectionArgs - 你可以在选择中包括 '?s' ,它们将按照它们出现在选择中的顺序由selectionArgs中的值替换。这些值将被绑定为字符串。
  • sortOrder - 如何对行进行排序,格式为SQL ORDER BY子句(不包括ORDER BY本身)。传递null将使用默认的排序顺序,可能是无序的。
// If non-null, this is the current filter the user has provided.
String curFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, so we don't care about the ID.
    // First, pick the base URI to use depending on whether we are
    // currently filtering.
    Uri baseUri;
    if (curFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(curFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}

onLoadFinished

当之前创建的Loader已经完成加载时此方法被调用。这个方法保证Loader提供最后的数据之前被调用。此时,你应该移除所有的老数据(因为它很快被释放),但是不应该你自己释放数据,因为它的Loader拥有它并会照顾。

一旦应用长时间不使用loader将释放数据。例如,如果数据是来自CursorLoader的一个cursor,你不应该自己调用close()。如果cursor放在CursorAdapter中,你应该用swapCursor()方法这样老的cursor不关闭。例如:

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    adapter.swapCursor(data);
}

onLoaderReset

当已经创建的Loader正在重置是调用这个方法,从而使数据不可用。这个回调让你找出数据何时被释放,这样你就可以移除它的引用。

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    adapter.swapCursor(null);
}

小结:

        本篇文章主要是介绍Loader API的使用和说明,都是来至于官方文档。在维护的模块和项目

开发中经常要用到Loader,所以在这里先记录下来。



 



 

            

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值