在使用AsyncTaskLoader的时候,遇到两个问题:
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(); }
<p>版权声明:本文为博主原创文章,未经博主允许不得转载。</p>