装载器从android3.0开始引进。它使得在activity或fragment中异步加载数据变得简单。装载器具有如下特性:
-
它们对每个Activity和Fragment都有效。
-
他们提供了异步加载数据的能力。
-
它拥有一个数据改变通知机制,当数据源做出改变时会及时通知。
-
当Cursor 发生变化时,会自动加载数据,因此并不需要再重新进行数据查询。
android设计Loader的初衷是想让大家像CursorLoader的做法一样,通过loader去维护数据,每次启动loader时先检查有没有旧的数据并把旧的数据先deliver给用户,然后再考虑要不要重新加载新的数据。
(二)、装载器API概述:
在使用装载器时,会涉及很多类和接口们,在下表中对它们总结一下:
Class/Interface | 说明 |
LoaderManager | 一个抽像类,关联到一个Activity或Fragment,管理一个或多个装载器的实例。这帮助一个应用管理那些与Activity或Fragment的生命周期相关的长时间运行的的操作。最常见的方式是与一个CursorLoader一起使用,然而应用是可以随便写它们自己的装载器以加载其它类型的数据。 每个activity或fragment只有一个LoaderManager。但是一个LoaderManager可以拥有多个装载器。 |
LoaderManager.LoaderCallbacks | 一个用于客户端与LoaderManager交互的回调接口。例如,你使用回调方法onCreateLoader()来创建一个新的装载器。 |
Loader(装载器) | 一个执行异步数据加载的抽象类。它是加载器的基类。你可以使用典型的CursorLoader,但是你也可以实现你自己的子类。一旦装载器被激活,它们将监视它们的数据源并且在数据改变时发送新的结果。 |
AsyncTaskLoader | 提供一个AsyncTask来执行异步加载工作的抽象类。 |
CursorLoader | AsyncTaskLoader的子类,它查询ContentResolver然后返回一个Cursor。这个类为查询cursor以标准的方式实现了装载器的协议,它的游标查询是通过AsyncTaskLoader在后台线程中执行,从而不会阻塞界面。使用这个装载器是从一个ContentProvider异步加载数据的最好方式。相比之下,通过fragment或activity的API来执行一个被管理的查询就不行了。 |
(三)、类目录结构:
1、API11中开始加入Loader:
java.lang.Object
↳ android.content.Loader<D>
↳ android.content.AsyncTaskLoader<D>
子类:
java.lang.Object
↳ android.content.Loader<D>
↳ android.content.AsyncTaskLoader<D>
↳ android.content.CursorLoader
2、为了兼容1.6以下版本:
java.lang.Object
↳ android.support.v4.content.Loader<D>
↳ android.support.v4.content.AsyncTaskLoader<D>
子类:
java.lang.Object
↳ android.support.v4.content.Loader<D>
↳ android.support.v4.content.AsyncTaskLoader<D>
↳ android.support.v4.content.CursorLoader
二、AsyncTaskLoader实例:
(一)、AsyncTaskLoader 实现数据加载的步骤:
1、窗体Activity要实现LoaderManager.LoaderCallbacks<Cursor>接口。至于继承于Activity还是FragmentActivity要看是否需要支持3.0以下版本。如果需要兼容则继承于FragmentActivity。
2、创建LoaderManager对象:通过getLoaderManager()或getSupportLoaderManager()方法来实现。如果是继承于FragmentActivity类,则使用getSupportLoaderManager()方法来创建LoaderManger对象,否则使用前者创建即可;
3、初始化LoaderManager对象:调用initLoader()方法来初始化;
- initLoader()方法有以下参数:
-
- 一个唯一ID来标志装载器
- 可选的参数,用于装载器初始化时
- 一个LoaderManager.LoaderCallbacks的实现。被LoaderManager调用以报告装载器的事件。一般窗体都实现了这个接口,所以传的是它自己:this;
- initLoader()保证一个装载器被初始化并激活.它具有两种可能的结果:
-
- 如果ID所指的装载器已经存在,那么这个装载器将被重用;
- 如果装载器不存在,initLoader()就触发LoaderManager.LoaderCallbacks中的回调方法onCreateLoader()。这是实例化并返回一个新Loader的地方。
5、自定义Loader,作为onCreateLoader()的返回值(也就是说onCreateLoader()方法必须返回自定义Loader的实例);
- 自定义Loader要继承于AsyncTaskLoader<Cursor>;
- 必须要有构造方法;
- 必须重写onStartLoading()、loadInBackground() 、deliverResult()。而且要在onStartLoading中调用forceLoad()才能依次调用下一个即将执行的方法。
-
- 在loadInBackground()方法中执行数据库查询,返回Cursor;
- 在deliverResult()方法中执行跟适配器交换数据的操作。adapter.swapCursor(data)。
- Flags used to determine the behavior of the adapter; may be any combination of
FLAG_AUTO_REQUERY
andFLAG_REGISTER_CONTENT_OBSERVER。
- FLAG_AUTO_REQUERY(常量值:1 )从 API11 开始已经废弃。因为他会在应用程序的 UI 线程中执行游标查询操作, 导致响应缓慢甚至应用程序无响应(ANR)的错误。作为替代方案,请使用 LoaderManager 和 AsyncTaskLoader、CursorLoader。
- 如果设置FLAG_REGISTER_CONTENT_OBSERVER(常量值:2),适配器会在Cursor上注册一个内容观测器,当通知到达时会调用 onContentChanged() 方法。
当一个已创建的装载器被重置从而使其数据无效时,此方法被调用.此回调使你能发现什么时候数据将被释放。你可以释放对它的引用。
04-01 04:00:25.477: MainActivity: ==onCreate 04-01 04:00:25.701: LoaderCallbacks: ==onCreateLoader 04-01 04:00:25.705: AsyncTaskLoader: ==onStartLoading 04-01 04:00:25.709: AsyncTaskLoader: ==loadInBackground 04-01 04:00:25.721: AsyncTaskLoader: ==deliverResult 04-01 04:00:25.721: LoaderCallbacks: ==onLoadFinished 04-01 04:00:25.749: MainActivity: ==onStart 04-01 04:00:25.749: MainActivity: ==onResume 04-01 04:00:25.973: MainActivity: ==onCreateOptionsMenu
04-01 04:01:06.693: MainActivity: ==onPause 04-01 04:01:08.413: MainActivity: ==onStop 04-01 04:01:15.721: AsyncTaskLoader: ==onStartLoading 04-01 04:01:15.721: AsyncTaskLoader: ==loadInBackground 04-01 04:01:15.721: AsyncTaskLoader: ==deliverResult 04-01 04:01:15.721: LoaderCallbacks: ==onLoadFinished 04-01 04:01:15.757: MainActivity: ==onStart 04-01 04:01:15.757: MainActivity: ==onResume
(三)、示例代码:
public class MainActivity extends Activity implements LoaderCallbacks<Cursor>{
private ListView listView;
private TextView textView;
private SimpleCursorAdapter adapter;
private LoaderManager loaderManager;
private static SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView) findViewById(R.id.listview);
textView=(TextView) findViewById(R.id.textviewinfo);
adapter=new SimpleCursorAdapter(MainActivity.this, R.layout.activity_item,
null, new String[]{"_id","name"},
new int[]{R.id.textView_id,R.id.textView_name},SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.setAdapter(adapter);
listView.setEmptyView(textView);
String path=Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"Download"
+File.separator+"info.db";
db=SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
loaderManager=getLoaderManager();
loaderManager.initLoader(1, null, this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/*
* 点击选项菜单打开操作sdcard中的数据表的应用
* 改应用操作完数据后显示当前应用的列表 观察数据变化
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
Intent intent=new Intent();
intent.setAction("com.sqq.action");
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new MyAsynTaskLoader(MainActivity.this);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
/**
* 继承AsyncTaskLoader自定义自己的异步加载loader
* 自定义的AsyncTaskLoader是静态内部类
*/
public static class MyAsynTaskLoader extends AsyncTaskLoader<Cursor>{
// 构造方法必须存在
public MyAsynTaskLoader(Context context) {
super(context);
}
/**
* 当AsyncTaskLoader创建成功后启动加载数据的方法 启动加载
*/
@Override
protected void onStartLoading() {
super.onStartLoading();
System.out.println("------ThreadId-------"+Thread.currentThread().getId());
forceLoad();//必须要添加 表示强制向下执行 强制执行后台的异步任务加载数据
}
/**
* 后台开启工作线程执行耗时操作的方法 耗时操作 查询数据库多条数据
*/
@Override
public Cursor loadInBackground() {
System.out.println("------ThreadId-------"+Thread.currentThread().getId());
Cursor cursor=db.rawQuery("select * from person", null);
return cursor;
}
/**
* 执行完loadInBackground之后执行次方法 接受loadInBackground的结果
*/
@Override
public void deliverResult(Cursor data) {
super.deliverResult(data);
System.out.println("------ThreadId-------"+Thread.currentThread().getId());
}
}
三、CursorLoader实例:
(一)、CursorLoader 实现数据加载的步骤:
1、窗体Activity要实现LoaderManager.LoaderCallbacks<Cursor>接口。至于继承于Activity还是FragmentActivity要看是否需要支持3.0以下版本。如果需要兼容则继承于FragmentActivity。
2、创建LoaderManager对象:通过getLoaderManager()或getSupportLoaderManager()方法来实现。如果是继承于FragmentActivity类,则使用getSupportLoaderManager()方法来创建LoaderManger对象,否则使用前者创建即可;
3、初始化LoaderManager对象:调用initLoader()方法来初始化;
- initLoader()方法有以下参数:
-
- 一个唯一ID来标志装载器
- 可选的参数,用于装载器初始化时
- 一个LoaderManager.LoaderCallbacks的实现。被LoaderManager调用以报告装载器的事件。一般窗体都实现了这个接口,所以传的是它自己:this;
- initLoader()保证一个装载器被初始化并激活.它具有两种可能的结果:
-
- 如果ID所指的装载器已经存在,那么这个装载器将被重用;
- 如果装载器不存在,initLoader()就触发LoaderManager.LoaderCallbacks中的回调方法onCreateLoader()。这是实例化并返回一个新Loader的地方。
【备注:】new CursorLoader()的参数:
1、 uri —要获取的内容的URI;
2、projection —要返回的列组成的数组。传入null 将会返回所有的列,但这样会导致低效;
3、selection —表明哪些行将被返回,相当于SQL语句中的WHERE条件 (不包括WHERE关键词)。传入null 将返回所有的行;
4、selectionArgs —Where语句中的'?’组成的数组。
5、sortOrder —如何排序,相当于SQL语句中的 ORDER BY 语句(不包括ORDER BY关键词)。传入null将使用默认顺序。
(二)、示例代码:
public class MainActivity extends Activity implements LoaderCallbacks<Cursor>{
private ListView listView;
private TextView textView;
private LoaderManager loaderManager;//声明loadermanger对象 管理loader
private SimpleCursorAdapter adapter;
private static final String TAG="MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView) findViewById(R.id.listview);
textView=(TextView) findViewById(R.id.textviewInfo);
loaderManager=getLoaderManager();//获得loadermanger对象
/* 第一个参数表示 唯一标示loader对象的id
* 第二个参数表示 表示创建loader是否需要传递参数 不需要则为null
* 第三个参数表示 表示LoaderCallbacks<Cursor>回调接口
*
* 如果指定id的loader存在则直接使用最后一个loader
* 如果指定id不存在则通过LoaderCallbacks<Cursor>回调接口中的onCreateLoader回调方法创建
*/
Bundle bundle=new Bundle();
bundle.putString("str", "传递到loader中的参数");
loaderManager.initLoader(1, bundle, this);//通过LoaderManger初始化loader对象
//构造方法中的flags必须设置为FLAG_REGISTER_CONTENT_OBSERVER
adapter=new SimpleCursorAdapter(MainActivity.this,R.layout.activity_item, null,
new String[]{"address","body"},
new int[]{R.id.textView_address,R.id.textView_body},
SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.setAdapter(adapter);
listView.setEmptyView(textView);//表示如果listview中没有数据展示参数中指定的控件中的内容
}
/**
* 根据指定的id创建loader对象
* 第一个参数 指定的id 就是initLoader中指定的id
* 第二个参数 loadermanger创建loader时传递的参数
*/
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
/*第一个参数 上下文对象
*第二个参数 表示查询contentprovider对象
*第三个参数 表示查询的字段数组
*第四个参数 表示查询的条件
*第五个参数 表示查询条件的占位符取值
*第六个参数 表示排序字段
*/
CursorLoader cursorLoader=new CursorLoader(MainActivity.this, Uri.parse("content://sms"),
null, null, null, null);
Log.i(TAG, "----------onCreateLoader"+args.getString("str"));
return cursorLoader;
}
/**
* 表示loader加载完毕时回调的方法 需要将新的数据进行更新替换
* 第一个表示 当前加载完毕的loader对象
* 第二个表示 当前加载完毕后的数据对象
*/
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);//当loader加载完毕得到数据之后将适配中的数据替换
Log.i(TAG, "----------onLoadFinished");
}
/**
* 当loader退出或者重置时回调的方法
*/
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);//清空适配器中的数据
Log.i(TAG, "----------onLoaderReset");
}
}