一、 CursorAdapter的介绍
需要由子类实现的两个方法:
public abstract View newView(Context context, Cursor cursor,ViewGroup parent);
public abstract void bindView(View view, Context context, Cursorcursor);
对外提供的公共方法:
//解除原有cursor的监听,为newCursor设置DataSetObserver和ContentObserver
publicCursor changeCursor(Cursor newCursor)
实现的BaseAdapter的抽象方法:
public int getCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
} else {
return 0;
}
}
public Object getItem(int position) {
if (mDataValid && mCursor != null) {
mCursor.moveToPosition(position);
return mCursor;
} else {
return null;
}
}
public long getItemId(int position) {
if (mDataValid && mCursor != null) {
if (mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIDColumn);
} else {
return 0;
}
} else {
return 0;
}
}
public View getView(int position, View convertView, ViewGroup parent) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
二、 ThreadCursorAdapter
1. 需求:
View的数据源来自数据库中,使用CursorAdapter来加载数据,因为使用cursor获取数据的操作可能会耗时,因此将这个操作放在工作线程中执行。
2. 策略
策略是在HandlerThread的handleMessage()中加载数据(泛型的数据),加载完成后在主线程的Handler中调用bindView()更新界面。针对cursor的所有操作都加了保护锁。它继承自BaseAdapter,同时又含有一个CursorAdapter的成员对象,数据来源于cusrosr(初始化时由参数传入)。重点关注一下BaseAdapter的getView()方法的实现,使用了View的tag来缓存数据,保存在tag中的数据如下:
private class LoadContainer {
WeakReference<View> view; //表示convertView
int position; //position表示getView中的每个child的位
T bind_object;//包含供getView中每一个child使用的数据
Adapter owner;//表示当前adapter
boolean loaded;//表示是否加载成功
long generation;
}
同时对外提供了两个方法:
//当cursor发生改变时响应处理,这里调用的是.
//mCursorAdapter的changeCursor()方法
public void changeCursor(Cursor cursor)
//在activity销毁时取消handlerThread的thread.
public void quitThread()
3. 实现
维护的对象:
private Context mContext;
//同步锁,加到了与cursor相关的操作上。
private Object mCursorLock = new Object();
private CursorAdapter mCursorAdapter;
//标识cursor是否为null
private boolean mHasCursor;
构造方法:
public ThreadedCursorAdapter(Contextcontext, Cursor c) {
mContext = context;
mHasCursor = (c != null);
mCursorAdapter = new CursorAdapter(context, c, 0){
...
}
mSize = mCursorAdapter.getCount();
//启动HandlerThread
mThread = new HandlerThread("threaded_adapter_" + this,
Process.THREAD_PRIORITY_BACKGROUND);
mThread.start();
}
重载的几个方法:
@Override
public int getCount() {
return mSize;
}
//item对象是一个cursor,它通过mCursorAdapter获取
@Override
public Cursor getItem(int position) {
return (Cursor)mCursorAdapter.getItem(position);
}
//获取position对应的cursor的”_id”列的值
@Override
public long getItemId(int position) {
synchronized (mCursorLock) {
return getItemId(getItem(position));
}
}
重点是下面的getView()方法:
@Override
public View getView(int position, ViewconvertView, ViewGroup parent){
//如果convert为null,则从newView()中加载布局
if (convertView == null) {
convertView = newView(mContext, parent);
}
//从tag中获取container对象,如果为null,则新建一个LoadContainer对象,并放到tag中。
LoadContainer container = (LoadContainer)convertView.getTag(R.id.load_object);
if (container == null) {
container = new LoadContainer();
container.view = new WeakReference<View>(convertView);
convertView.setTag(R.id.load_object, container);
}
//当从tag中获取container时,可能走if,否则执行else
if (container.position == position
&& container.owner == this
&& container.loaded
&& container.generation ==mGeneration) {
bindView(convertView,container.bind_object);
}else {
//这里传递给bindView的参数T为新创建的类对象,成员均为空。
//此处数据还没有载入,因此只是空加载。
bindView(convertView, cachedLoadObject());
if (mHasCursor) {
container.position = position;
container.loaded = false;
container.owner = this;
container.generation = mGeneration;
}
mLoadHandler.obtainMessage(position, container).sendToTarget();
}
return convertView;
}
在HandlerThread的mLoadHandler中为container.bind_object赋值,这里为什么要使用多线程来执行handleMessage()? 因为它里面执行的操作是从数据库里面读取数据。
mLoadHandler = new Handler(mThread.getLooper()) {
@SuppressWarnings("unchecked")
@Override
public void handleMessage(Message msg) {
loadRowObject(msg.what, (LoadContainer) msg.obj);
}
};
private void loadRowObject(int position, LoadContainer container) {
synchronized (mCursorLock) {
Cursor c = (Cursor) mCursorAdapter.getItem(position);
if (c == null ||c.isClosed()) {
return;
}
//使用cursor为bind_object中的各项赋值
container.bind_object=getRowObject(c, container.bind_object);
}
//然后调用主线程的handler更新container中的loader为true,重
//新调用bindView(),这个时候执行的才是有数据的加载。
mHandler.obtainMessage(position, container).sendToTarget();
}
private T cachedLoadObject() {
if (mLoadingObject == null) {
mLoadingObject = getLoadingObject();
}
return mLoadingObject;
}
以下几个方法需要在继承者中来实现:
public abstract View newView(Contextcontext, ViewGroup parent);
public abstract void bindView(View view, Tobject);
public abstract T getRowObject(Cursor c, TrecycleObject);
public abstract T getLoadingObject();
protected abstract long getItemId(Cursorc);