即使应用程序处于非活动状态,CursorLoader如何自动更新视图?

时间:2013-03-20 07:44:08

标签: android android-loadermanager android-cursorloader

我一直在研究一个小的待办事项列表应用程序。我使用CursorLoader从内容提供者更新ToDolistview。我有一个函数onNewItemAdded(),当用户在文本视图中输入一个新项并单击Enter时调用该函数。请参阅以下内容:

public void onNewItemAdded(String newItem) {
    ContentResolver cr = getContentResolver();

    ContentValues values = new ContentValues();
    values.put(ToDoContentProvider.KEY_TASK, newItem);

    cr.insert(ToDoContentProvider.CONTENT_URI, values);
    // getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

@Override
protected void onResume() {
    super.onResume();
    //getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    CursorLoader loader = new CursorLoader(this,
            ToDoContentProvider.CONTENT_URI, null, null, null, null);
    Log.e("GOPAL", "In the onCreateLoader");
    return loader;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK);
    Log.e("GOPAL", "In the onLoadFinished");
    todoItems.clear();
    if (cursor.moveToNext() == false) Log.e("GOPAL", "Empty Cursor");
    else {
        while (cursor.moveToNext()) {
            ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex));
            todoItems.add(newItem);
        }
        aa.notifyDataSetChanged(); // aa is arrayadapter used for the listview
    }
}

我已经读过,只要内容提供程序db中有数据更改,CursorLoader就会自动更新视图。这意味着我想,只要数据发生变化,就必须隐式调用getLoaderManager().restartLoader(0, null, this),对吧?           但那并没有发生。每当我添加一个新项目(该项目从onNewItemAdded添加到数据库中,但未显式调用restartLoader时),请暂停此活动并将其恢复。我没有看到对restartLoader的任何隐式调用(即使db已更改),并且listview也没有更新添加新项目。这是为什么?即使应用程序未处于活动状态,CursorLoader如何自动更新视图? 谢谢:))

编辑:我还在插入内容提供商时使用了getContext().getContentResolver().notifyChange(insertedId, null)

3 个答案:

答案 0 :(得分:71)

我找到了问题的答案。通常,CursorLoader不会自动检测数据更改并将其加载到视图中。我们需要跟踪URI以进行更改。这可以通过以下步骤完成:

  1. 使用以下内容通过游标在内容解析程序中注册Observer :(在ContentProvider的查询方法中完成)
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

  2. 现在,当使用insert() / delete() / update(),对URI基础数据进行任何更改时,我们会使用以下内容通知ContentResolver有关更改的内容:

    getContext().getContentResolver().notifyChange(insertedId, null);

  3. 观察者收到了这个消息,我们在步骤1中注册了这个调用ContentResolver.query(),调用了ContentProvider的{​​{1}}方法来返回一个新的游标到query()LoaderManager调用LoaderManager传递此光标,以及onLoadFinished()我们用新数据更新视图(使用CursorLoader)。

  4. 对于自定义AsyncTaskLoaders:

    有时我们需要自定义加载器而不是CursorLoader。在这里,我们可以使用除光标之外的其他对象来指向加载的数据(如列表等)。在此我们不会通过游标通知ContentResolver。应用程序也可能没有内容提供程序来跟踪URI更改。在这种情况下,我们使用BroadcastReceiver或显式ContentObserver来实现自动视图更新。具体如下:

    1. 我们需要定义扩展Adapter.swapCursor()的自定义加载器并实现其所有抽象方法。与AsyncTaskLoader不同,我们的自定义加载程序可能会也可能不会使用内容提供程序,并且当此加载程序被实例化时,它的构造函数可能不会调用CursorLoader。所以我们使用广播接收器来达到目的。
    2. 我们需要在抽象ContentResolver.query()类的BroadCastReceiver方法中实例化ContentObserverOnStartLoading()
    3. 应该定义此BroadCast接收器以从内容提供者或任何系统事件(如安装新应用程序)接收数据更改广播,并且必须调用加载器的AsyncTaskLoader方法,以通知加载器有关数据更改的信息。 Loader会自动完成其余工作以加载更新的数据并调用onContentChanged()来更新视图。
    4. 有关详细信息,请参阅:http://developer.android.com/reference/android/content/AsyncTaskLoader.html

      我发现这对于明确的解释非常有用:http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

答案 1 :(得分:3)

好吧,我认为您可以在某些事件上重启加载程序。例如。在我的情况下,我有一个TODO的活动。点击&#39;添加&#39;选项,它会启动新活动,该活动具有提供新TODO的视图。

我在父活动的onActivityResult()

中使用以下代码
getLoaderManager().restartLoader(0, null, this);

对我来说很好。如果有更好的方法,请分享。

答案 2 :(得分:2)

在初始化时获取对加载程序的引用,如下所示

 Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);

然后创建一个扩展ContentObserver的类,如下所示

 class DataObserver extends ContentObserver {

    public DataObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        dayWeatherLoader.forceLoad();
    }
}

然后在onResume生命周期方法中注册内容观察者,如下所示

@Override
public void onResume() {
    super.onResume();
    getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}

每当内容提供者的基础数据发生变化时,将调用contentobserver的onChange方法,您可以要求加载器再次加载数据