本文主要从Loader入手, 去分析Loader使用、Loader的源码分析等,主要分为以下四篇:
本文基于原生的 Android8.0源码进行分析。
产生背景
在Android3.0之前,很多应用程序响应性能方面有缺陷。UI切换之间的小故障、activity切换延迟、ANR问题。响应性能方面的故障大多数来源于此事实—-大多数开发者在UI线程中执行了耗时操作,其中很常用的一个就是通过网络或者本地数据库进行数据加载,用这种方式载入数据是很差的选择。
比如,在Loaders之前,cursors主要通过两个Activity方法(现在已经过时deprecated)来进行管理和查询:
public void startManagingCursor(Cursor)
public Cursor managedQuery(Uri, String, String, String, String)
由于API本身的这种设计,也会给开发者一种误导:我应该在这里进行cursor查询操作。有经验的开发者都知道在这里进行异步操作,比如自己控制子线程或者AsyncTask,处理完了通过Handler回调给主线程,但是把这些交给开发者去做,开发者去管理这些也会比较费事费力,于是Google在Android3.0引入了Loader和LoaderManager类来简化该过程。
Loaders确保所有的cursor操作是异步的,从而排除了UI线程中堵塞的可能性。而且,通过LoaderManager来管理,Loaders还可以在activity实例中保持当前的cursor数据,也就是不需要重新查询(比如,当因为横竖屏切换需要重新启动activity时)。另外,当数据改变时,Loaders可以自动检测底层数据的更新和重新检索。
Loader的简单使用
Loader 机制的 使用场景 有:
加载联系人
加载短信
网络加载数据
我们主要讲对于本地资源的加载,也就是访问本地数据库,获取cursor。我们先来看一下Loader的使用,主要分为两步:
1、创建自己的Loader
2、实现在Activity或者Fragment上LoaderCallbacks接口
下面将通过一个demo来具体介绍Loader的使用, 不过为了节省篇幅,只贴出了代码主干部分, 完全的demo请参考文末的地址。
1、 创建自己的Loader
Google已经封装得很好了,使用Loader通常都是继承自AsyncTaskLoader或者它的子类, 如CursorLoader就是AsyncTaskLoader的子类,源码中使用CursorLoader及其子类比较多。 下面看一个简单的例子及必须实现的方法:
public class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
……
@Override
public List<AppEntry> loadInBackground() {
//省略具体的代码
//最核心的方法,也是必须实现的方法
//此处用来去真正加载数据, 数据加载完成后返回数据
//数据最终会回调给 回调接口的onLoadFinished 方法
……
List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
……
//返回我们的数据泛型的类型
return entries;
}
@Override
public void deliverResult(List<AppEntry> apps) {
//此方法可不实现,但是如果实现了此方法,则必须调用super. deliverResult
//正常流程都需要调用父类的deliverResult, 此方法用来分发数据
//把loadInBackground加载完的数据回调到onLoadFinished方法中
super.deliverResult(apps);
}
/**
* 子类必须实现的方法, 启动Loader调用
*/
@Override
protected void onStartLoading() {
if (mApps != null) {
deliverResult(mApps);
}
//如果想监听数据源, 可以再开始加载数据前,去设置一个数据监听,当数据源变化, 就回调给自己设置的监听
if (mAppsObserver == null) {
mAppsObserver = new InstalledAppsObserver(this);
}
if (takeContentChanged()) {
//如果想真正开始加载数据, 则必须调用forceLoad()方法
forceLoad();
} else if (mApps == null) {
forceLoad();
}
}
@Override
protected void onStopLoading() {
// The Loader has been put 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.
}
/**
* 重置 Loader
*/
@Override
protected void onReset() {
// Ensure the loader is stopped.
onStopLoading();
// At this point we can release the resources associated with 'apps'.
if (mApps != null) {
releaseResources(mApps);
mApps = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mAppsObserver != null) {
getContext().unregisterReceiver(mAppsObserver);
mAppsObserver = null;
}
if (mLocaleObserver != null) {
getContext().unregisterReceiver(mLocaleObserver);
mLocaleObserver = null;
}
}
@Override
public void onCanceled(List<AppEntry> apps) {
super.onCanceled(apps);
// The load has been canceled, so we should release the resources
// associated with 'mApps'.
releaseResources(apps);
}
/**
* 可以不用实现,父类通过此方法去加载数据,
* 内部会调用 loadInBackground()
*/
@Override
public void forceLoad() {
super.forceLoad();
}
……
}
2、在Activity或者Fragment中实现LoaderCallbacks回调接口
LoaderCallbacks接口主要有三个方法, 实现如下:
public class LoaderDemoFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<List<AppEntry>> {
@Override
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
//这个方法主要用来创建我们需要的Loader
//在这里我们还可以做一些想做的事
return new AppListLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
//数据加载完毕会回调到这个方法,
mAdapter.setData(data);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<List<AppEntry>> loader) {
//该方法将在 先前创建的加载器重置 且 数据因此不可用 时调用,
//通过此回调我们可以了解何时将释放数据,因此能够及时移除其引用。
mAdapter.setData(null);
}
/**
* 找一个合适的地方, 初始化Loader
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 参数:id ,唯一标识, Bundle,额外的数据, LoaderCallbacks接口
getLoaderManager().initLoader(LOADER_ID, null, this);
}
}
Loader的使用流程大致就是这样,详细的代码可以去这个地址下载demo:demo下载
后面讲继续分析Loader的源码部分,了解Loader实现原理。