[解決済み] 新しくアタッチされたオブザーバーに対して、LiveDataオブザーバーが2回トリガーされる理由
質問
私の理解では
LiveData
は、データの現在の状態の変化に対してオブザーバを起動し、 データの一連の履歴の状態変化には起動しないということです。
現在、私は
MainFragment
を実行する
Room
を変更するために、書き込み操作
トラッシュされていないデータ
に変更する。
トラッシュされたデータ
.
また、別の
TrashFragment
を観察しています。
ゴミになったデータ
.
次のようなシナリオを考えてみましょう。
- 現在、0件の ゴミ箱に捨てられたデータ .
-
MainFragment
は現在アクティブなフラグメントです。TrashFragment
はまだ作成されていません。 -
MainFragment
追加された1 ゴミデータ . - 現在、1つの ゴミになったデータ
-
ナビゲーションドロワーを使って
MainFragment
をTrashFragment
. -
TrashFragment
のオブザーバは、まずonChanged
で、0 ゴミになったデータ -
もう一度
TrashFragment
のオブザーバは二番目にonChanged
で、1 ゴミになったデータ
予想外なのは、(6)のようなことは起こらないはずだということです。
TrashFragment
は最新の
ゴミになったデータ
であり、これは1である。
以下は私のコードです。
TrashFragment.java
public class TrashFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
noteViewModel = ViewModelProviders.of(getActivity()).get(NoteViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
noteViewModel.getTrashedNotesLiveData().removeObservers(this);
noteViewModel.getTrashedNotesLiveData().observe(this, notesObserver);
MainFragment.java
public class MainFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
noteViewModel = ViewModelProviders.of(getActivity()).get(NoteViewModel.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
noteViewModel.getNotesLiveData().removeObservers(this);
noteViewModel.getNotesLiveData().observe(this, notesObserver);
NoteViewModel .java
public class NoteViewModel extends ViewModel {
private final LiveData<List<Note>> notesLiveData;
private final LiveData<List<Note>> trashedNotesLiveData;
public LiveData<List<Note>> getNotesLiveData() {
return notesLiveData;
}
public LiveData<List<Note>> getTrashedNotesLiveData() {
return trashedNotesLiveData;
}
public NoteViewModel() {
notesLiveData = NoteplusRoomDatabase.instance().noteDao().getNotes();
trashedNotesLiveData = NoteplusRoomDatabase.instance().noteDao().getTrashedNotes();
}
}
ルームを処理するコード
public enum NoteRepository {
INSTANCE;
public LiveData<List<Note>> getTrashedNotes() {
NoteDao noteDao = NoteplusRoomDatabase.instance().noteDao();
return noteDao.getTrashedNotes();
}
public LiveData<List<Note>> getNotes() {
NoteDao noteDao = NoteplusRoomDatabase.instance().noteDao();
return noteDao.getNotes();
}
}
@Dao
public abstract class NoteDao {
@Transaction
@Query("SELECT * FROM note where trashed = 0")
public abstract LiveData<List<Note>> getNotes();
@Transaction
@Query("SELECT * FROM note where trashed = 1")
public abstract LiveData<List<Note>> getTrashedNotes();
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract long insert(Note note);
}
@Database(
entities = {Note.class},
version = 1
)
public abstract class NoteplusRoomDatabase extends RoomDatabase {
private volatile static NoteplusRoomDatabase INSTANCE;
private static final String NAME = "noteplus";
public abstract NoteDao noteDao();
public static NoteplusRoomDatabase instance() {
if (INSTANCE == null) {
synchronized (NoteplusRoomDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
NoteplusApplication.instance(),
NoteplusRoomDatabase.class,
NAME
).build();
}
}
}
return INSTANCE;
}
}
を受信しないようにする方法はありますか?
onChanged
を2回受け取らないようにする方法はありますか?
デモ
この問題を実証するために、デモプロジェクトを作成しました。
ご覧のように、書き込み操作(
ゴミ箱に入れたノートを追加
ボタンをクリック) した後
MainFragment
に切り替えたとき
TrashFragment
に切り替えたとき、私は
onChanged
で
TrashFragment
は一度だけ呼び出されます。しかし、2回呼び出されています。
デモプロジェクトは以下からダウンロードできます。 https://github.com/yccheok/live-data-problem
どのように解決するのですか?
私はあなたのプロジェクトをフォークして、少しテストしました。私が言えることは、あなたは重大なバグを発見したということです。
再現と調査を容易にするために、私はあなたのプロジェクトを少し編集しました。更新されたプロジェクトをここで見ることができます。 https://github.com/techyourchance/live-data-problem . また、あなたのレポにプルリクエストをオープンしました。
これが気づかれないようにするために、私はまた 課題を作成しました。 を Google の課題追跡システムで公開しました。
再現のための手順
- MainFragmentでREPRODUCE_BUGがtrueに設定されていることを確認する。
- アプリをインストールする
- ゴミ箱のメモを追加するボタンをクリックします。
- TrashFragmentに切り替えます。
- 正しい値を持つLiveDataの通知フォームが1つだけあったことに注意してください。
- MainFragmentに切り替えます。
- ゴミ箱に入れたノートを追加するボタンをクリックします。
- TrashFragmentに切り替えます。
- LiveData から 2 つの通知があり、1 つ目の通知は不正確な値であることに注意してください。
REPRODUCE_BUG を false に設定すると、バグが再現されないことに注意してください。 再現されません。のLiveDataへのサブスクリプションが、TrashFragmentの挙動を変えたことを実証しています。 のサブスクリプションがTrashFragmentの動作を変更したことを示しています。
期待した結果です。どのような場合でも正しい値で1つだけ通知されます。 以前のサブスクリプションによる動作の変更はありません。
さらに詳しい情報です。ソースを少し見てみましたが、どうやら LiveData のアクティブ化と新しいオブザーバー サブスクリプションの両方が原因で Observerのサブスクリプションの両方によって引き起こされる通知のようです。ComputableLiveDataがonActive()計算をExecuteにオフロードする方法と関係があるかもしれません。 がonActive()の計算をExecutorにオフロードする方法と関係があるかもしれません。
関連
-
[解決済み] HttpPostによる画像送信
-
[解決済み] CardView layout_width="match_parent "が親のRecyclerViewの幅と一致しない。
-
[解決済み] handler.postDelayed()を停止する。
-
[解決済み] アプリ内課金テスト:android.test.purchased already owned
-
[解決済み] バイト配列の画像ファイルをビットマップに変換するには?
-
[解決済み] XMLで矩形を描画できますか?
-
[解決済み] Studio 3.4 をアップデートしたら、引数の leftShift() メソッドが見つかりませんでした。
-
[解決済み] 複数のフィルタを持つBroadcastReceiverか、複数のBroadcastReceiverか?
-
[解決済み] 文字列リソースにHTML?
-
[解決済み] WhatsAppでメッセージを送信する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 深くネストされたスタックから離れるとき、Fragmentのバックスタックをクリーンアップする方法はこれで良いのでしょうか?
-
[解決済み] wrap_contentでRelativeLayoutがフルスクリーンになってしまう
-
[解決済み] Android Studioの「未実装メソッドの追加」機能
-
[解決済み] アンドロイドのクライアントでヒープアップデートを有効にする方法
-
[解決済み] 非ActivityクラスでContextを取得する [重複].
-
[解決済み] アンドロイドでシェイクを検出するには?
-
[解決済み] Android StudioからADBを手動で再起動する方法
-
[解決済み] アンドロイドのdatepickerダイアログで最大の日付を設定するには?
-
[解決済み] RecyclerViewのアイテムに波及効果を追加する
-
[解決済み] Android APKファイルの中身を見るには?