web-dev-qa-db-ja.com

アプリがアクティブでない場合でも、CursorLoaderはどのようにしてビューを自動的に更新しますか?

私は小さなTo-Doリストアプリに取り組んでいます。 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
    }
}
_

私が読んだ、CursorLoaderは、コンテンツプロバイダーのデータベースでデータが変更されるたびに、ビューを自動的に更新します。つまり、getLoaderManager().restartLoader(0, null, this)は、データが変更されるたびに暗黙的に呼び出される必要があると思いますよね?しかし、それは起こっていません。新しいアイテムを追加するたびに(アイテムはonNewItemAddedからdbに追加されますが、restartLoaderは明示的に呼び出されません)、このアクティビティを一時停止して再開します。 (dbが変更されている場合でも)restartLoaderへの暗黙の呼び出しはなく、リストビューも新しい項目が追加されて更新されていません。何故ですか?アプリがアクティブでない場合でも、CursorLoaderはどのようにしてビューを自動的に更新しますか?ありがとう:)

編集:私はコンテンツプロバイダーの挿入でもgetContext().getContentResolver().notifyChange(insertedId, null)を使用しました。

24

私の質問に対する答えが見つかりました。一般に、CursorLoaderはデータの変更を自動的に検出し、それらを読み込んで表示しません。変更のURIを追跡する必要があります。これは次の手順で実行できます。

  1. 次を使用したカーソルによるコンテンツリゾルバーへのオブザーバーの登録:(ContentProviderのクエリメソッドで完了)
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

  2. ここで、insert()/delete()/update(),を使用してURIの基礎となるデータに変更がある場合、次の方法でContentResolverに変更について通知します。

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

  3. これはオブザーバーによって受け取られ、ステップ1で登録され、ContentResolver.query()が呼び出され、次にContentProviderquery()メソッドが呼び出されて、LoaderManagerに新しいカーソルが返されます。 LoaderManagerは、onLoadFinished()を呼び出してこのカーソルを渡し、CursorLoaderでビューを更新します(Adapter.swapCursor()を使用して)新鮮なデータで更新します。

カスタムAsyncTaskLoadersの場合:

CursorLoaderの代わりにカスタムローダーが必要になる場合があります。ここでは、カーソル以外の別のオブジェクトを使用して、ロードされたデータをポイントできます(リストなど)。この場合、カーソルを介してContentResolverに通知する権限はありません。 URIの変更を追跡するために、アプリケーションにコンテンツプロバイダーがない場合もあります。このシナリオでは、BroadcastReceiverまたは明示的なContentObserverを使用して、ビューの自動更新を実現します。これは次のとおりです。

  1. AsyncTaskLoaderを拡張し、そのすべての抽象メソッドを実装するカスタムローダーを定義する必要があります。 CursorLoaderとは異なり、カスタムローダーはコンテンツプロバイダーを使用する場合と使用しない場合があり、このローダーがインスタンス化されている場合、そのコンストラクターがContentResolver.query()を呼び出さない場合があります。そのため、放送受信機を使用して目的を果たします。
  2. 抽象BroadCastReceiverクラスのOnStartLoading()メソッドでContentObserverまたはAsyncTaskLoaderをインスタンス化する必要があります。
  3. このBroadCastレシーバーは、コンテンツプロバイダーまたは任意のシステムイベント(インストールされた新しいアプリと同様)からデータ変更ブロードキャストを受信するように定義する必要があり、ローダーのonContentChanged()メソッドを呼び出して、ローダーにデータの変更を通知する必要があります。ローダーは残りを自動的に実行して更新されたデータをロードし、onLoadFinished()を呼び出してビューを更新します。

詳細については、これを参照してください: http://developer.Android.com/reference/Android/content/AsyncTaskLoader.html

これは明確な説明に非常に役立つことがわかりました: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

72

まあ、特定のイベントでローダーを再起動できると思います。例えば。私の場合、私はTODOの活動をしています。 「追加」オプションをクリックすると、新しいTODOをフィードするビューを持つ新しいアクティビティが起動します。

親アクティビティのonActivityResult()で次のコードを使用しています

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

それは私にとってはうまくいきます。より良いアプローチがある場合は共有してください。

3
iuq

次のように初期化中にローダーへの参照を取得します

 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メソッドが呼び出され、ローダーにデータを再度ロードするよう要求できます。

2
rijogeorge7