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

Posted

技术标签:

【中文标题】即使应用程序处于非活动状态,CursorLoader 如何自动更新视图?【英文标题】:How do CursorLoader automatically updates the view even if the app is inactive? 【发布时间】:2013-03-09 05:10:00 【问题描述】:

我一直在开发一个小型待办事项列表应用程序。我使用 CursorLoader 从内容提供者更新 ToDolistview。我写了一个函数onNewItemAdded(),当用户在文本视图中输入一个新项目并点击回车时调用它。参考如下:

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
    

我已阅读,只要内容提供程序数据库中的数据发生更改,CursorLoader 就会自动更新视图。这意味着我想,getLoaderManager().restartLoader(0, null, this) 必须在数据发生变化时被隐式调用,对吧? 但这并没有发生。每当我添加一个新项目(该项目从onNewItemAdded 添加到数据库中,但没有明确调用restartLoader)时,暂停此活动并恢复它。我没有看到对 restartLoader 的任何隐式调用(即使 db 已更改),并且列表视图也不会随着添加的新项目而更新。这是为什么?即使应用程序不活动,CursorLoader 如何自动更新视图??? 谢谢:)

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

【问题讨论】:

【参考方案1】:

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

    通过游标在内容解析器中注册一个观察者:(在ContentProvider的查询方法中完成)cursor.setNotificationUri(getContext().getContentResolver(), uri);

    现在,当使用 insert()/delete()/update(), 的 URI 基础数据发生任何更改时,我们会使用以下命令通知 ContentResolver 更改:

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

    这是由观察者接收的,我们在步骤 1 中注册并调用 ContentResolver.query(),然后调用 ContentProviderquery() 方法以将新光标返回到 LoaderManagerLoaderManager 调用 onLoadFinished() 传递此游标,以及使用新数据更新视图(使用 Adapter.swapCursor())的 CursorLoader

对于自定义 AsyncTaskLoaders:

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

    我们需要定义自定义加载器,它扩展AsyncTaskLoader 并实现其所有抽象方法。与CursorLoader 不同,我们的自定义加载器可能会或可能不会使用内容提供程序,并且它的构造函数可能不会调用ContentResolver.query(),当这个加载器被实例化时。所以我们使用广播接收器来达到这个目的。 我们需要在抽象AsyncTaskLoader类的OnStartLoading()方法中实例化一个BroadCastReceiverContentObserver。 此广播接收器应定义为接收来自内容提供者或任何系统事件的数据更改广播(如安装的新应用程序),它必须调用加载程序的onContentChanged() 方法,以通知加载程序有关数据更改。 Loader 自动完成其余工作以加载更新的数据并调用onLoadFinished() 来更新视图。

更多详情请参考:http://developer.android.com/reference/android/content/AsyncTaskLoader.html

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

【讨论】:

【参考方案2】:

好吧,我认为您可以在某些事件上重新启动加载程序。例如。就我而言,我有一项 TODO 活动。单击“添加”选项时,它会启动新的活动,该活动具有提供新 TODO 的视图。

我在父活动的 onActivityResult() 中使用以下代码

getLoaderManager().restartLoader(0, null, this);

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

【讨论】:

这个看起来不错,最简单,对我有用,但有更好的方法吗?【参考方案3】:

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

 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()));

当content provider的底层数据发生变化时,会调用contentobserver的onChange方法,让loader再次加载数据

【讨论】:

以上是关于即使应用程序处于非活动状态,CursorLoader 如何自动更新视图?的主要内容,如果未能解决你的问题,请参考以下文章

即使处于非活动状态,Hangfire也会继续运行SQL查

检测应用程序处于后台/非活动状态时是不是触发了本地通知

如何处理 HTTP 412(前提条件失败)- 设备处于非活动状态

如何强制 Aero 绘制无边框窗口,就好像它处于活动状态一样,即使它不是?

应用程序处于非活动状态时无法接收静默通知 iOS

通过Swift中的触摸检测应用程序何时处于活动状态或非活动状态[重复]