您的位置:首页 > 移动开发 > Android开发

Android Loader

2015-12-09 00:35 866 查看
Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:

1、They are available to every Activity and Fragment. //支持Activity和Fragment

2、They provide asynchronous loading of data. //异步下载

3、They monitor the source of their data and deliver new results when the content changes. //当数据源改变时能及时通知客户端

4、They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data. //发生configuration change时自动重连接

用法相当简单,首先看一个例子

public class CursorLoaderActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>  {

ArrayList<Person> persons;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);

lv.setAdapter(new MyAdapter());

}
};

Uri uri = Uri.parse("content://com.exanple.liaoli.db/person");

@Override
protected void onResume() {
super.onResume();

getLoaderManager().initLoader(0, null, this);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this,uri,null,null,null,null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(cursor == null){
return;
}

persons = new ArrayList<>();

while (cursor.moveToNext()){

String pName = cursor.getString(cursor.getColumnIndex("Name"));
String pPhone = cursor.getString(cursor.getColumnIndex("phone"));
int pCount = cursor.getInt(cursor.getColumnIndex("account"));
String pSix = cursor.getString(cursor.getColumnIndex("six"));
Person p = new Person(pName,pPhone,pCount,pSix);
persons.add(p);
}

cursor.close();

Message.obtain(handler, 0).sendToTarget();
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {

}

class MyAdapter extends BaseAdapter {

@Override
public int getCount() {
return persons.size();
}

@Override
public Object getItem(int position) {
return persons.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

View v = null;

if(convertView == null){

v = View.inflate(CursorLoaderActivity.this,R.layout.lv_item,null);

}else{

v = convertView;

}

TextView name = (TextView) v.findViewById(R.id.name);

TextView phone = (TextView) v.findViewById(R.id.phone);
TextView count = (TextView) v.findViewById(R.id.count);
TextView six= (TextView) v.findViewById(R.id.six);
Person p = persons.get(position);

name.setText(p.getName());
phone.setText(p.getPhone());
count.setText(p.getCount() + "");
six.setText(p.getSix());
return v;
}
}

ListView lv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_dbdate);

lv = (ListView) findViewById(R.id.lv);

}

这里只是一个简单的从数据库,然后展示在listview上。

从代码上看来,通过实现LoaderManager.LoaderCallbacks就行了.

在onCreateLoader里面实现你要请求的耗时操作,当异步线程操作完成之后就会从onLoadFinished返回数据.

getLoaderManager().initLoader(0, null, this);

getLoaderManager 是获得一个LoaderManager 实例,但是LoaderManager
是一个抽象类,他的一个实例是LoaderManagerImpl是它的一个内部类

通过源码我们知道这里获取的其实就是LoaderManagerImpl实例

LoaderManagerImpl getLoaderManagerImpl() {
if (mLoaderManager != null) {
return mLoaderManager;
}
mCheckedForLoaderManager = true;
mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
return mLoaderManager;
}


下面我们来看LoaderManager
的类结构







LoaderManagerImpl是LoaderManager的实现类,并且是LoaderManager的内部类,

LoaderInfo是LoaderManagerImpl的内部类,LoaderInfo类实现了Loader.OnLoadCompleteListener<Object>接口

LoaderManager.LoaderCallbacks<Cursor>
是LoaderManager的内部接口

这里为了方便说明略去了很多东西,但是并不会影响我们分析,其原理,我们这里只分析了第一次启用的情况,所以会忽略很多其他的东西,

首先

getLoaderManager().initLoader(0, null, this);


这是我们Loader的启动点。
前面我们从源码知道getLoaderManager其实就是获取一个LoaderManagerImpl的实例,所以这个initLoader方法也就是调用的此实例的方法

下面我们就来看一下LoaderManagerImpl的initLoader的方法的具体实现

/**
* Call to initialize a particular ID with a Loader.  If this ID already
* has a Loader associated with it, it is left unchanged and any previous
* callbacks replaced with the newly provided ones.  If there is not currently
* a Loader for the ID, a new one is created and started.
*
* <p>This function should generally be used when a component is initializing,
* to ensure that a Loader it relies on is created.  This allows it to re-use
* an existing Loader's data if there already is one, so that for example
* when an {@link Activity} is re-created after a configuration change it
* does not need to re-create its loaders.
*
* <p>Note that in the case where an existing Loader is re-used, the
* <var>args</var> given here <em>will be ignored</em> because you will
* continue using the previous Loader.
*
* @param id A unique (to this LoaderManager instance) identifier under
* which to manage the new Loader.
* @param args Optional arguments that will be propagated to
* {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
* @param callback Interface implementing management of this Loader.  Required.
* Its onCreateLoader() method will be called while inside of the function to
* instantiate the Loader object.
*/
@SuppressWarnings("unchecked")
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}

LoaderInfo info = mLoaders.get(id);

if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);

if (info == null) {
// Loader doesn't already exist; create.
info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, "  Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}

if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}

return (Loader<D>)info.mLoader;
}


如果我们传入的id对应的Loadinfo 不为,此时将传入的callback 赋值给空Loadinfo对应的成员变量 ,如果Loadinfo 有数据,此时

就会调用Loadinfo 的callOnLoadFinished方法

void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
if (mHost != null) {
lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
mCallbacks.onLoadFinished(loader, data);
} finally {
if (mHost != null) {
mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
}
}


此方法最关键额一句

mCallbacks.onLoadFinished(loader, data);

这一句代码就是将数据放回到实现了LoaderManager.LoaderCallbacks的Activity或者Fragment.在拿到数据后就可以更新UI了

下面再看Loadinfo
为空的情况

如果Loadinfo
为空了,此时会走这一句代码

info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);

下面我们看createAndInstallLoader方法

private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
Loader<Object> loader = callback.onCreateLoader(id, args);
info.mLoader = (Loader<Object>)loader;
return info;
}

private LoaderInfo createAndInstallLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
try {
mCreatingLoader = true;
LoaderInfo info = createLoader(id, args, callback);
installLoader(info);
return info;
} finally {
mCreatingLoader = false;
}
}

void installLoader(LoaderInfo info) {
mLoaders.put(info.mId, info);
if (mStarted) {
// The activity will start all existing loaders in it's onStart(),
// so only start them here if we're past that point of the activitiy's
// life cycle
info.start();
}
}


关键代码

Loader<Object> loader = callback.onCreateLoader(id, args);

createLoader()方法中的这一句代码,其实就是我们在Activity或者Fragment中实现LoaderManager.LoaderCallbacks的onCreateLoader方法

installLoader()方法首先是将我们创建的Loadinfo
添加到SparseArray的实例mLoaders中,然后回去调用Loadinfo 的start()方法

void start() {
if (mRetaining && mRetainingStarted) {
// Our owner is started, but we were being retained from a
// previous instance in the started state...  so there is really
// nothing to do here, since the loaders are still started.
mStarted = true;
return;
}

if (mStarted) {
// If loader already started, don't restart.
return;
}

mStarted = true;

if (DEBUG) Log.v(TAG, "  Starting: " + this);
if (mLoader == null && mCallbacks != null) {
mLoader = mCallbacks.onCreateLoader(mId, mArgs);
}
if (mLoader != null) {
if (mLoader.getClass().isMemberClass()
&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {
throw new IllegalArgumentException(
"Object returned from onCreateLoader must not be a non-static inner member class: "
+ mLoader);
}
if (!mListenerRegistered) {
mLoader.registerListener(mId, this);
mLoader.registerOnLoadCanceledListener(this);
mListenerRegistered = true;
}
mLoader.startLoading();
}
}

此方法首先做一些相关的检查工作,例如,Loader是不是静态的?是不是内部类的?当前Loader是否注册了回调事件,没注册会给其注册等。

最关键的一句是

mLoader.startLoading();

这一句是我们的Loader要开始工作,我们先看Loader的类结构



LoadTask是AsyncTaskLoader的内部内,他是AsyncTask子类,所有异步操作有他完成

接下来我们进入Loader的startLoading()方法

public final void startLoading() {
mStarted = true;
mReset = false;
mAbandoned = false;
onStartLoading();
}

/**
* Subclasses must implement this to take care of loading their data,
* as per {@link #startLoading()}.  This is not called by clients directly,
* but as a result of a call to {@link #startLoading()}.
*/
protected void onStartLoading() {
}


发现onStartLoading()是一个空方法,那么我们找子类AsyncTaskLoader,发现AsyncTaskLoader未重写该方法

再去找CursorLoader,CursorLoader重写了onStartLoading(),如下

@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}


deliverResult(mCursor);

会触发Loader<D>.OnLoadCompleteListener<D>接口的

onLoadComplete(Loader<D> loader, D data)方法,LoadInfo类实现了他,这里效果和LoaderManagerImpl的initLoader的方法中info
不为空的情形一样 。

关键是forceLoad()(Loader中的实现)

public void forceLoad() {
onForceLoad();
}


发现onForceLoad
是一个空方法,

protected void onForceLoad() {
}


AsyncTaskLoader重写了此方法,CursorLoader未重写

@Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
mTask = new LoadTask();
if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
executePendingTask();
}


此方法创建了一个LoadTask异步任务,并调用了executePendingTask()方法

void executePendingTask() {
if (mCancellingTask == null && mTask != null) {
if (mTask.waiting) {
mTask.waiting = false;
mHandler.removeCallbacks(mTask);
}
if (mUpdateThrottle > 0) {
long now = SystemClock.uptimeMillis();
if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
// Not yet time to do another load.
if (DEBUG) Log.v(TAG, "Waiting until "
+ (mLastLoadCompleteTime+mUpdateThrottle)
+ " to execute: " + mTask);
mTask.waiting = true;
mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
return;
}
}
if (DEBUG) Log.v(TAG, "Executing: " + mTask);
mTask.executeOnExecutor(mExecutor, (Void[]) null);
}
}

此方法代码看的人头皮发麻啊,不过不要紧我们其他的不看,只看下面一句

mTask.executeOnExecutor(mExecutor, (Void[]) null);

也就是执行异步任务,

此时会调用LoadTask类的doInBackground()方法

@Override
protected D doInBackground(Void... params) {
if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
try {
D data = AsyncTaskLoader.this.onLoadInBackground();
if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
return data;
} catch (OperationCanceledException ex) {
if (!isCancelled()) {
// onLoadInBackground threw a canceled exception spuriously.
// This is problematic because it means that the LoaderManager did not
// cancel the Loader itself and still expects to receive a result.
// Additionally, the Loader's own state will not have been updated to
// reflect the fact that the task was being canceled.
// So we treat this case as an unhandled exception.
throw ex;
}
if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
return null;
}
}


doInBackground()方法中做的是在AsyncTaskLoader的onLoadInBackground()方法中,我们去看

protected D onLoadInBackground() {
return loadInBackground();
}

public abstract D loadInBackground();


loadInBackground()是一个抽象方法,CursorLoader实现了他,发现其实就是在查询数据库哦,哈哈哈。

@Override
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}


当doInBackground()方法走完之后,就来到了方法。

/* Runs on the UI thread */
@Override
protected void onPostExecute(D data) {
if (DEBUG) Log.v(TAG, this + " onPostExecute");
try {
AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
} finally {
mDone.countDown();
}
}



下面这句是线程相关的东西,我们不需要关心,有兴趣可以百度一下。

mDone.countDown();


我们找到AsyncTaskLoader的dispatchOnLoadComplete()方法

void dispatchOnLoadComplete(LoadTask task, D data) {
if (mTask != task) {
if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
dispatchOnCancelled(task, data);
} else {
if (isAbandoned()) {
// This cursor has been abandoned; just cancel the new data.
onCanceled(data);
} else {
commitContentChanged();
mLastLoadCompleteTime = SystemClock.uptimeMillis();
mTask = null;
if (DEBUG) Log.v(TAG, "Delivering result");
deliverResult(data);
}
}
}


关键代码

deliverResult(data);

我们看此方法的实现

public void deliverResult(D data) {
if (mListener != null) {
mListener.onLoadComplete(this, data);
}
}


mListener是 OnLoadCompleteListener<D>的实例,这个实例实在LoadInfo 的start()方法中赋值的,而LoadInfo 也实现了该接口,我们来看onLoadComplete()方法在LoadInfo中的实现

@Override
public void onLoadComplete(Loader<Object> loader, Object data) {
if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);

if (mDestroyed) {
if (DEBUG) Log.v(TAG, "  Ignoring load complete -- destroyed");
return;
}

if (mLoaders.get(mId) != this) {
// This data is not coming from the current active loader.
// We don't care about it.
if (DEBUG) Log.v(TAG, "  Ignoring load complete -- not active");
return;
}

LoaderInfo pending = mPendingLoader;
if (pending != null) {
// There is a new request pending and we were just
// waiting for the old one to complete before starting
// it.  So now it is time, switch over to the new loader.
if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);
mPendingLoader = null;
mLoaders.put(mId, null);
destroy();
installLoader(pending);
return;
}

// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}

//if (DEBUG) Log.v(TAG, "  onLoadFinished returned: " + this);

// We have now given the application the new loader with its
// loaded data, so it should have stopped using the previous
// loader.  If there is a previous loader on the inactive list,
// clean it up.
LoaderInfo info = mInactiveLoaders.get(mId);
if (info != null && info != this) {
info.mDeliveredData = false;
info.destroy();
mInactiveLoaders.remove(mId);
}

if (mHost != null && !hasRunningLoaders()) {
mHost.mFragmentManager.startPendingDeferredFragments();
}


关键代码

callOnLoadFinished(loader, data);

这里又调用了callOnLoadFinished,

void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
if (mHost != null) {
lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
mCallbacks.onLoadFinished(loader, data);
} finally {
if (mHost != null) {
mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
}
}


终于可已将获取的数据给实现了LoaderManager.LoaderCallbacks的Activity或者Fragment
mCallbacks.onLoadFinished(loader, data);

下面的方法调用的时序图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  CursorLoader Loader