以下是个人见解,不喜勿喷。
Loader 装载器,自android3.0开始引进。他使得在activity或fragment中异步加载数据变得简单。他有以下特性:
- 他们对每个Activity和Fragment都有效
- 他们提供了异步加载数据的能力
- 他们监控数据源的一举一动并在内容改变时传递新的结果
- 当由于配置改变而被重新创建后,他们自动重练到上一个加载器的游标,所以不必重新查询数据
在使用装载器的时候,会用到下面API:
- LoaderManager 负责管理多个loader
- LoaderManager.LoaderCallbacks 回调接口
- Loader 装载器
- AsyncTaskLoader 异步任务转载器
- CursorLoader AsnycTaskLoader的子类,为查询cursor以标准的方式实现了装载器的协议。
下面以一个列子来说明怎么用(PS:这里只是简单地使用),先看下效果图
下面来看下代码。我将所有的数据都放在数据库中,并且集合contentprovider使用。
实体类
public class Person {
private int id;
private String name;
private String age;
/**
* 对应的数据库biao名 以及列名
*/
public static final String TABLE_NAME="tb_person";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_AGE = "age";
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
DBopenhelper类,只创建了一张数据表而已。
public class PersonDbOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME= "person.db";
private static final int DB_VERSION= 1;
private static PersonDbOpenHelper mHelper;
public static PersonDbOpenHelper getInstance(Context context){
if (mHelper==null){
synchronized (PersonDbOpenHelper.class){
if (mHelper==null){
mHelper = new PersonDbOpenHelper(context);
}
}
}
return mHelper;
}
private PersonDbOpenHelper(Context context) {
super(context.getApplicationContext(),DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table " + Person.TABLE_NAME +" ("+
" _id integer primary key autoincrement , "+
Person.COLUMN_NAME + " text, "+
Person.COLUMN_AGE +" text "+
")";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
provider
public class PersonProvider extends ContentProvider {
private static final String AUTHORITY = "com.gl.person.provider.personprovider";
public static final Uri URI_PERSOM_ALL = Uri.parse("content://"+AUTHORITY+"/person");
private static UriMatcher matcher;
private static final int PERSON_ALL = 0;
private static final int PERSON_ONE = 1;
static{
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY,"person",PERSON_ALL);
matcher.addURI(AUTHORITY,"person/#",PERSON_ONE);
}
private PersonDbOpenHelper mHelper;
private SQLiteDatabase mDb;
@Override
public boolean onCreate() {
mHelper = PersonDbOpenHelper.getInstance(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
int match = matcher.match(uri);
switch (match){
case PERSON_ALL:
break;
case PERSON_ONE:
long id = ContentUris.parseId(uri);
selection="_id = ?";
selectionArgs = new String[]{String.valueOf(id)};
break;
default:
throw new IllegalArgumentException("Wrong Uri:"+uri);
}
mDb = mHelper.getReadableDatabase();
Cursor c= mDb.query(Person.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(),URI_PERSOM_ALL);
return c;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int match = matcher.match(uri);
if (match!=PERSON_ALL){
throw new IllegalArgumentException("Wrong Uri:"+uri);
}
mDb = mHelper.getReadableDatabase();
long rowId = mDb.insert(Person.TABLE_NAME,null,values);
if (rowId>0){
notifyDataSetChanged();
return ContentUris.withAppendedId(uri,rowId);
}
return null;
}
private void notifyDataSetChanged() {
getContext().getContentResolver().notifyChange(URI_PERSOM_ALL,null);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
我在这里实现了数据库的插入和查询操作。关于ContentProvider我这里不再细说。
下面重点戏来了。。
主界面我只用了一个ListView。我们来看看Loader怎么用。
private void initLoader() {
getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(MainActivity.this,PersonProvider.URI_PERSOM_ALL,null,null,null,null);
return loader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
});
}
这里我们初始化了Loader,并重写LoaderManager.LoaderCallbacks。
- onCreateLoader 在loader被创建时候调用
- onLoadFinished 在loader load完毕时候调用
- onLoaderReset 在loader reset时候调用。
这里的mAdapter是自己实现CursorAdapter的,稍后会说到。这个swapCursor方法是干什么用的呢。我们看下源码
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*
* @param newCursor The new cursor to be used.
* @return Returns the previously set Cursor, or null if there wasa not one.
* If the given new Cursor is the same instance is the previously set
* Cursor, null is also returned.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
Cursor oldCursor = mCursor;
if (oldCursor != null) {
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (newCursor != null) {
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
return oldCursor;
}
就是将 将要用的新的游标替换就得,说白了就是游标下移(个人理解),并且通知数据变化。到这里就有那么一点点感觉了。。。
再来看下Adapter
public class PersonAdapter extends CursorAdapter {
public PersonAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
}
//返回布局
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item,parent,false);
return view;
}
//给view绑定数据
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView tv_name = (TextView) view.findViewById(R.id.person_name);
TextView tv_age = (TextView) view.findViewById(R.id.person_age);
tv_name.setText("姓名:"+cursor.getString(cursor.getColumnIndex(Person.COLUMN_NAME)));
tv_age.setText("年龄:"+cursor.getString(cursor.getColumnIndex(Person.COLUMN_AGE)));
}
}
只需重写2个方法,newView,返回子view,bindView,绑定数据。
,没错,用法就是这么简单粗暴。不过,还有更复杂的用法等着我们。
代码下载地址:Github