«

Android AsyncTaskLoader需要注意的问题

时间:2024-3-2 19:37     作者:韩俊     分类: Android


在使用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>

标签: android

热门推荐