Android AsyncTaskLoader需要注意的问题
2015-06-29 21:38
627 查看
在使用AsyncTaskLoader的时候,遇到两个问题:
1.继承AsyncTaskLoader并实现了必要的方法后,发现loadInBackground()没有被执行
在网上查找之后,得到如下解决方法:
继承AsyncTaskLoader后,需要重载以下方法供系统调用:
2.使用问题1的解决方案后,Loader开始工作了,但是又遇到了新的问题,我在Activity里面写了一个ListView,ListView的数据通过AsyncTaskLoader
来进行加载,当从当前Activity跳到另一个Activity并按back键返回时,此时没有问题,但是当点击ListView中的某一项时,报出以下的错误:
E/AndroidRuntime(1141): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(16908298,
class android.widget.ListView) with Adapter(class com.study.ActivityAdapter)]
以下是我自定义AsyncTaskLoader的代码:
更新ListView数据的操作都是发生在UI线程,所以应该是系统在我们不知道的地方更新了数据而没有通知ListView.
查看Activity.java的源码可以看到:
Activity恢复的时候,会重新调用onStart函数,从而最终调用到我们Loader里的onStartLoading函数,
而我们的onStartLoading函数是这样:
也就是说,不管如何,都会在后台将数据重新加载一遍,因为是系统调用的,所以没有通知ListView,造成数据的不同步.
到这里,解决方案就呼之欲出了:
改造如下:
1.继承AsyncTaskLoader并实现了必要的方法后,发现loadInBackground()没有被执行
在网上查找之后,得到如下解决方法:
继承AsyncTaskLoader后,需要重载以下方法供系统调用:
@Override protected void onStartLoading() { // TODO Auto-generated method stub super.onStartLoading(); forceLoad(); }
2.使用问题1的解决方案后,Loader开始工作了,但是又遇到了新的问题,我在Activity里面写了一个ListView,ListView的数据通过AsyncTaskLoader
来进行加载,当从当前Activity跳到另一个Activity并按back键返回时,此时没有问题,但是当点击ListView中的某一项时,报出以下的错误:
E/AndroidRuntime(1141): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(16908298,
class android.widget.ListView) with Adapter(class com.study.ActivityAdapter)]
以下是我自定义AsyncTaskLoader的代码:
public class ActivityListLoader extends AsyncTaskLoader<Integer>{
private Context context;
private Bundle args;
private String path;
private List<Map<String, Object>> data;
public ActivityListLoader(Context context,Bundle args) {
super(context);
this.context = context;
this.args = args;
path = args.getString("path");
data = new ArrayList<Map<String,Object>>();
}
@Override
public Integer loadInBackground() {
PackageManager pm = context.getPackageManager();
Intent matchIntent = new Intent();
matchIntent.setAction(Constants.ACTION_MAIN);
matchIntent.addCategory(Constants.CATEGORY_STUDY);
matchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
List<ResolveInfo> list = pm.queryIntentActivities(matchIntent,0);
String label = null;
for(ResolveInfo info:list)
{
label = info.loadLabel(pm).toString();
if(label.startsWith(path))
{
String[] paths = path.split("/");
String[] files = label.split("/");
Map<String, Object> map = new HashMap<String, Object>();
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
int pathlen = paths.length;
if(path.equals(""))
{
pathlen = 0;
}
if(files.length - pathlen == 1)
{
map.put("isFile", true);
intent.setComponent(new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
map.put("intent", intent);
}else
{
map.put("isFile", false);
intent.setClass(context, MainActivity.class);
intent.putExtra("path", path+files[pathlen]+"/");
map.put("intent", intent);
}
data.add(map);
}
}
return 0;
}
@Override protected void onStartLoading() { // TODO Auto-generated method stub super.onStartLoading(); forceLoad(); }
public List<Map<String, Object>> getData()
{
return data;
}
}
MainActivity的代码:
public class MainActivity extends ListActivity implements
LoaderCallbacks<Integer> {
private ListActivity context;
private List<Map<String,Object>> datalist;
enum Type {
ShowProgress, DismissProgress,UpdateAdapter
};
private ProgressDialog progress;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Type type = (Type) msg.obj;
switch (type) {
case ShowProgress:
progress.show();
break;
case DismissProgress:
if(progress.isShowing())
progress.dismiss();
break;
case UpdateAdapter:
ActivityAdapter adapter = new ActivityAdapter(context, datalist);
setListAdapter(adapter);
adapter.notifyDataSetChanged();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
initProgressDialog();
Intent intent = getIntent();
String path = intent.getStringExtra("path");
if (path == null)
path = "";
Bundle args = new Bundle();
args.putString("path", path);
getLoaderManager().initLoader(0, args, this);
Message msg = Message.obtain(handler, 0, Type.ShowProgress);
msg.sendToTarget();
setListAdapter(new ActivityAdapter(this, new ArrayList<Map<String,Object>>()));
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) getListAdapter().getItem(position);
Intent intent = (Intent)map.get("intent");
startActivity(intent);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void initProgressDialog()
{
progress = new ProgressDialog(this);
progress.setMessage("加载所有的Activity信息");
progress.setCanceledOnTouchOutside(false);
}
@Override
public Loader<Integer> onCreateLoader(int id, Bundle args) {
return new ActivityListLoader(context, args);
}
@Override
public void onLoadFinished(Loader<Integer> loader, Integer data) {
ActivityListLoader aloader = (ActivityListLoader) loader;
datalist = aloader.getData();
LogUtils.e("debug");
Message msg = Message.obtain(handler, 0, Type.DismissProgress);
msg.sendToTarget();
Message msgUpdate = Message.obtain(handler, 0, Type.UpdateAdapter);
msgUpdate.sendToTarget();
}
@Override
public void onLoaderReset(Loader<Integer> loader) {
LogUtils.e("debug");
Message msgUpdate = Message.obtain(handler, 0, Type.UpdateAdapter);
msgUpdate.sendToTarget();
}
}
更新ListView数据的操作都是发生在UI线程,所以应该是系统在我们不知道的地方更新了数据而没有通知ListView.
查看Activity.java的源码可以看到:
protected void onStart() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this); mCalled = true; if (!mLoadersStarted) { mLoadersStarted = true; if (mLoaderManager != null) { mLoaderManager.doStart(); //系统会执行Loader的onStartLoading函数 } else if (!mCheckedForLoaderManager) { mLoaderManager = getLoaderManager(null, mLoadersStarted, false); } mCheckedForLoaderManager = true; } getApplication().dispatchActivityStarted(this); }
Activity恢复的时候,会重新调用onStart函数,从而最终调用到我们Loader里的onStartLoading函数,
而我们的onStartLoading函数是这样:
@Override protected void onStartLoading() { // TODO Auto-generated method stub super.onStartLoading(); forceLoad(); }
也就是说,不管如何,都会在后台将数据重新加载一遍,因为是系统调用的,所以没有通知ListView,造成数据的不同步.
到这里,解决方案就呼之欲出了:
改造如下:
@Override protected void onStartLoading() { // TODO Auto-generated method stub super.onStartLoading(); if(data.size() == 0) forceLoad(); }
相关文章推荐
- Xamarin.Android开发实践(一)
- Android Studio导入project慢的原因
- android 虚拟键盘的显示与隐藏问题
- 有道词典
- Android SDK更新镜像服务器
- Android下集成Paypal支付
- Android调用相机并将照片存储到sd卡上
- Android 内存分析
- Android 开发工具类 33_开机自运行
- android4.4 魔趣 使用go备份的解决方案。
- 如何修改android系统字体大小
- 音频焦点 (audio focus)(二)
- Android Studio 项目混淆打包时,报错Caused by: java.lang.NoSuchMethodError: android.util.Xml.newPullParser
- Android学习 16-> 列表控件ListView
- Android 音频焦点(Audio Focus)
- android selector android:state_enabled 无效
- Android虚拟机Dalvik Android的相关文件类型
- Android基础知识_Context的理解及使用
- android 自定义折线图
- android中一种不常见的函数调用方式