Android Loaders(二)Loader的使用
转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/24094733
参考翻译自:https://docs.google.com/presentation/d/1_5puFz6kUK1cSYvTmJbvQFYpgj8LXZECBrn65w62UKk/edit#slide=id.p
1. 简单的API让你的Activity/Fragment可以和Loaders进行交互。
2. 每个Activity和每个Fragment只有一个LoaderManager的实例,他们不共享这些Loaders。
3. 主要的方法:
initLoader(intid, Bundle args, LoaderCallbacks<D> callbacks)
restartLoader(intid, Bundle args, LoaderCallbacks<D> callbacks)
destroyLoader(intid)
getLoader(intid)
privatefinal LoaderCallbacks<Result> loaderCallbacks
= newLoaderCallbacks<Result>() {
@Override
public Loader<Result>onCreateLoader(int id, Bundle args) {
return newMyLoader(getActivity(), args.getLong("id"));
}
@Override
public voidonLoadFinished(Loader<Result> loader, Result result) {
handleResult(result);
}
@Override
public voidonLoaderReset(Loader<Result> loader) {
}};
注意:绝对不要直接调用一个Loader的框架方法,而你需要使用LoaderManager来自动调用。
在Activity中
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
getSupportLoaderManager().initLoader(LOADER_ID,null, callbacks);
}
在Fragment中
@Override
publicvoid onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
getLoaderManager().initLoader(LOADER_ID,null, callbacks);
}
每个Loader有三个状态:started,stopped,reset。而我们的LoaderManager会自动的根据Activity或者Fragment的状态来改变这些Loader的状态。
1. 在Activity/FragmentstartsàLoader starts: onStartLoading()
2. Activity变成InVisible或者Fragment被detachedàLoader stops: onStopLoading()
3. Activity/Fragment被重新创建à没有回调。LoaderManager将会继续接收results,然后将结果保存到缓存。
4. Activity/Fragment被销毁或者restartLoader/destroyLoader被调用àLoader resets: onReset()
private void onNewQuery(String query) {
Bundleargs = new Bundle();
args.putString("query",query);
getLoaderManager().restartLoader(LOADER_ID,args, loaderCallbacks);
}
///////////////////////////////////
@Override
publicLoader<Result> onCreateLoader(int id, Bundle args) {
returnnew QueryLoader(getActivity(), args.getString("query"));
}
很多时候你都不需要使用参数,直接传入null。
LoaderCallBacks是你的Fragment/Activity的一部分。你也可以直接使用Fragment/Activity的实例变量。
@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.newsId= getArguments().getLong("newsId");
}
...
@Override
publicLoader<News> onCreateLoader(int id, Bundle args) {
returnnew NewsLoader(getActivity(), NewsFragment.this.newsId);
}
当一个Fragment因为configuration变化被重新创建的时候(比如旋转屏幕,改变语言等),你在onActivityCreated()里面调用了initLoader()之后,他的LoaderManager调用了两次onLoadFinished。
解决方案:
1. 如果代码逻辑允许,可以不用处理。
2. 将之前的result保存起来,检查结果是否有变化。
3. 在onCreate()里面调用setRetainInstance(true)。
很多时候,我们只想运行一次这个Loader一次。比如:我们点击一个按钮然后提交一些信息到服务器。在用户点击了一个按钮后,我们调用initLoader,这个时候用户旋转屏幕,在旋转完屏幕之后,我们想要使用之前loader得到的结果。注意在旋转屏幕的时候,我们的Loader还没有提交完数据。像我们之前用AsyncTask的话,是没有办法在Activity/Fragment重新创建之后拿到之前任务返回的result的。但是使用Loader就简单多了。
在LoaderCallbacks中加入下面的代码
@Override
publicvoid onLoadFinished(Loader<Integer> loader, Result result) {
getLoaderManager().destroyLoader(LOADER_ID);
... // Process the result
}
在Activity/Fragment的创建过程中加入:
@Override
publicvoid onActivityCreated(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//Reconnect to the loader only if present
if(getLoaderManager().getLoader(LOADER_ID) != null) {
getLoaderManager().initLoader(LOADER_ID,null, this);
}
}
1. 滥用loader id。在创建和restart一个loader的时候,不需要每次都去增加这个ID值。对于某种Loader,只需要一个唯一的id值。
2. 不需要在整个应用程序的范围类为每种Loader创建一个唯一的id值。因为每个LoaderManager都是独立的。你只需要保证你的Activity活着Fragment范围内这种Loader的ID值唯一就可以了。所以直接在Activity或者Fragment中创建一个私有的常量ID就可以了。
3. 避免出现FragmentManager exceptions
你不能在LoaderCallback这些回调里面直接创建FragmentTransaction(包括DialogFragment这种dialog)。
解决方法就是使用Handler来处理FragmentTransaction。
publicclass LinesFragment extends ContextMenuSherlockListFragment implementsLoaderCallbacks<List<LineInfo>>, Callback {
...
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler(this);
adapter = newLinesAdapter(getActivity());
}
@Override
public void onActivityCreated(BundlesavedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(adapter);
setListShown(false);
getLoaderManager().initLoader(LINES_LOADER_ID,null, this);
}
@Override
public Loader<List<LineInfo>>onCreateLoader(int id, Bundle args) {
return newLinesLoader(getActivity());
}
@Override
public voidonLoadFinished(Loader<List<LineInfo>> loader, List<LineInfo>data) {
if (data != null) {
adapter.setLinesList(data);
} else if (isResumed()) {
handler.sendEmptyMessage(LINES_LOADING_ERROR_WHAT);
}
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public voidonLoaderReset(Loader<List<LineInfo>> loader) {
adapter.setLinesList(null);
}
@Override
public boolean handleMessage(Messagemessage) {
switch (message.what) {
case LINES_LOADING_ERROR_WHAT:
MessageDialogFragment
.newInstance(R.string.error_title, R.string.lines_loading_error)
.show(getFragmentManager());
return true;
}
return false;
}