第二十一个项目——使用Loader实现异步数据加载操作
实验目的
掌握Loader框架的基本原理;
掌握AsyncTaskLoader、CursorLoader及LoaderManager等组件的使用方法;
实验要求
掌握使用Loader进行异步数据加载的方法;
实验内容
在第九个项目中,我们实现了通过读取SQLite数据库加载新闻列表,并为了简化读取数据库的操作定义了NewsCursorAdapter适配器,用于直接将数据库查询得到的Cursor游标对象作为适配器的数据源。
为了简便起见,这类数据读取操作放在MainActivity的活动组件中。然而实际项目开发中,无论数据是从数据库或是 云端服务器进行读取,这类数据操作通常会比较耗时,这势必会占用UI主线程资源,从而影响App与用户的交互体验。
如果通过AsyncTask异步活动在子线程中进行数据读取,则开发人员还需要在Activity或Fragment生命周期中 管理主线程、子线程的状态,这增加了应用开发的复杂度。
本实验中使用Android提供的Loader异步加载框架解决上述问题,将数据读取等操作放入Loader框架中进行管理, 又可降低在Activity或Fragment中管理数据读取子线程状态的复杂度。
在Loader异步加载框架中主要需要熟悉两个组件的使用方法,其一是LoaderManager,它用于在Activity或Fragement中管理多个Loader实例,每个Loader实例可对应一个AsyncTaskLoader对象;其二是AsyncTaskLoader,它在AsyncTask的基础上做了封装,每一个AsyncTaskLoader实例会启动一个AsyncTask,从而启动一个子线程执行数据异步操作。
步骤一,打开第九个项目
打开第九个项目,在该项目基础上完成本次实验。
步骤二,定义NewsQueryCursorLoader类
在Android Studio中新建一个名为NewsQueryCursorLoader的Java类,该类继承至CursorLoader (CursorLoader继承至AsyncTaskLoader)。在该类中,有两个方法需要进行重写,分别是 onStartLoading()及loadInBackground()方法,其中onStartLoading()方法调用了forceLoad()方法强制NewsQueryAsyncCursorLoader类实例重新执行loadInBackground()方法;
而loadInBackground()方法则是实际放入子线程执行的代码,在该方法中把数据库的查询操作代码放入其中,而该方法的返回值为Cursor类型,则是从子线程中返回的数据。loadInBackground()方法的返回值将作为LoaderManager.LoaderCallbacks接口的onLoadFinished()方法的参数回传给调用LoaderManager的组件。
以下为NewsQueryAsyncCursorLoader类定义:
步骤三,在MainActivity中实现LoaderManager.LoaderCallbacks接口
在Activity或Fragement中使用LoaderManager需要做的工作主要包括两部分:
实现LoaderManager.LoaderCallbacks接口,该接口需要实现onCreateLoader()、onLoaderFinished()、onLoaderReset()方法;分别用于当Loader实例被创建、完成、重置时执行的回调方法;
获取LoaderManager实例并调用其initLoader()或restartLoader()方法启动Loader实例;
通过在MainActivity中实现LoaderManager.LoaderCallbacks接口,该接口需要一个泛型参数,其指明接口中的三个方法返回值或参数类型:
Loader onCreateLoader(int i, Bundle bundle);
void onLoadFinished(Loader loader, D obj);
void onLoaderReset(Loader loader);
1. 实现LoaderManager.LoaderCallbacks接口
由于本实验中,数据从数据库进行查询,其返回值类型为Cursor,因此指明LoaderManager.LoaderCallbacks泛型参数为Cursor,具体代码见本文最后一段代码。
接下来逐一讲解LoaderManager.LoaderCallbacks接口的三个重写方法。
首先,重写Loader onCreateLoader(int i, Bundle bundle)方法,该方法的返回值为Loader类型,而第一个参数i表示的是Loader的编号,通常由LoaderManager的initLoader()或restartLoader()方法指定,开发者可根据该参数决定实例化哪个Loader类实例(实际开发中,可能需要多个Loader实例对数据进行不同操作,例如查询、更新、插入、删除等操作都可能由不同的自定义Loader子类完成;具体而言,本实验中NewsQueryAsyncCursorLoader实现对数据的查询操作,而插入操作则需要NewsInsertAsyncCursorLoader来完成);第二个参数是传递给Loader的Bundle类型值,在本实验中并未使用到。 onCreateLoader()方法在LoaderManager调用initLoader()或restartLoader()方法后被回调执行。
可以看到在MainActivity类中,为了简便起见,直接返回了一个匿名NewsQueryAsyncCursorLoader类对象。实际开发中通常需要通过switch或if语句判断参数i的值,从而实例化不同的Loader子类。
其次,重写void onLoadFinished(Loader loader, Cursor cursor)方法,该方法第一个参数为在onCreateLoader()函数返回的Loader(实际为NewsQueryAsyncCursorLoader)实例,第二个参数为Loader实例执行完成loadInBackground()函数后的返回值Cursor对象(也就是NewsQueryAsyncCursorLoader中查询到的数据库结果集Cursor对象)。该方法在对应的Loader实例的loadInBackground()方法执行完成后被回调执行。
在该方法中,通过cursorAdapter.swapCursor()方法切换了最新的数据库查询结果集Cursor对象,并调用notifyDataSetChanged()方法通知ListView刷新列表数据。
最后,重写void onLoaderReset(Loader loader)方法。该方法在对应的Loader被重置时或LoaderManager调用destroyLoader()方法或Activity、Fragement被销毁时被回调执行。通常需要在该方法内将引用到某一个Loader数据的对象进行删除。
2. 调用LoaderManager.initLoader()方法启动Loader
通过调用LoaderManager对象调用initLoader()方法启动Loader从而实现数据的异步读取操作,该方法接受3个参数,分别是:
int i,表示要初始化的Loader编号,开发人员可自定义每个Loader对应的整型编号,从而在onStartLoading()、onLoadFinished()等方法内进行判断执行相应的业务代码;
Bundle bundle,表示传递给onStartLoading()方法的Bundle类型参数,通常用于初始化Loader,具体视Loader子类实现而异;
Context context,表示上下文参数;
在Activity、Fragement等组件中,通过getLoaderManager()方法可获得LoaderManager实例。在MainActivity活动的onCreate()方法中,当ListView、NewsCursorAdapter等对象实例化完成后,调用了getLoaderManager()方法获取了LoaderManager实例,接着调用initLoader()方法初始化Loader实现新闻列表数据的异步读取操作。
以下为修改后的MainActivity类代码:
步骤五,编译并运行App
编译本项目,成功后在AVD上或物理机上运行App,其运行效果与第九个项目相同。
请思考如何结合Loader框架与SwipeRefreshLayout控件实现新闻列表的异步下拉刷新。
实验小结
通过本次实验,你应该掌握了如下知识内容:
使用AsyncTaskLoader、CursorLoader及LoaderManager等组件进行异步数据加载;
Last updated
Was this helpful?