Android Jetpack - LiveData とデータの注ぎ込み

7ea8cfa0dea64306aef3d792bde1292a.jpg

 1.ライブデータ

LiveData は、Android Jetpack パッケージによって提供される監視可能なデータ ストレージ クラスであり、オブザーバーを追加することで他のコンポーネントから監視できます。通常のオブザーバーとは異なり、LiveData の最も重要な特徴は、他のアプリケーション コンポーネント (アクティビティ、フラグメント、サービスなど) のライフ サイクルに従うライフ サイクル認識機能があることです。この認識により、LiveData はアクティブなライフサイクル状態にあるアプリ コンポーネント オブザーバーのみを更新するようになります。たとえば、アクティビティでデータが更新されたが、アクティビティがすでに破棄状態にある場合、LiveData はアクティビティ (オブザーバー) に通知しません。

さらに、LiveData には多くの利点があります。

①コンポーネントがアクティブ状態にあるときに、メモリリークを心配することなく、対応するメソッドをコールバックして、対応する UI を更新できます。

②設定によってアクティビティが再作成される場合、データの保存と回復を手動で処理する必要はありません。弊社用に梱包してあります。

③アクティビティがアクティブ状態ではないときに、アクティビティがアクティブ状態になるまでオブザーバーの onChange メソッドをコールバックするのを待つのではなく、setValue を呼び出した直後に Livedata がオブザーバーの onChange メソッドをコールバックするようにしたい場合は、observeForever メソッドを使用できます。 , ただし、onDestroy RemoveObserver の時点である必要があります。(ページのライフサイクルに関係なく、setValue または postValue の直後にデータに戻りたい場合は、observer() とそれほど変わらない、observerForever() メソッドを使用できます。AlwaysActiveObserver は GenericLifecycleObserver を実装していないため、ただし、これを使用した後は、onDestroy() メソッドの RemoveObserver() メソッドを忘れずに呼び出して LiveData の監視を停止する必要があることに注意してください。そうしないと、LiveData は常にアクティブになり、Activity は決して停止しません。システムによって自動的にアクティブ化されるため、リサイクルするとメモリ リークが発生する可能性があります。

イベント バスの代わりにイベント バスを実装するために使用できます。)

プロジェクトでこのような問題が頻繁に発生することを思い出してください。ネットワーク リクエストの結果が返されたときに、アクティビティまたはフラグメントが破棄されたかどうかを判断し、破棄されていない場合は UI を更新する必要があることがよくあります。Livedata を使用すると、Activity が onStart または onResume の状態にあるときに対応するコールバックが作成されるため、大量の activity.isDestroyed() を記述しなくても、この問題をうまく処理できます。

LiveData には、MutableLiveData と MediatorLiveData という 2 つのサブクラスがあります。MutableLiveData は観察する必要がある単一のデータをカプセル化しますが、MediatorLiveData は他の LiveData を観察できます。開発プロセスでは、通常、LiveData を継承する代わりに LiveData のサブクラスが使用されます。

LiveData は通常 ViewModel と一緒に使用されます。ViewModel はデータ更新のトリガーを担当します。更新は LiveData に通知され、その後 LiveData がアクティブなオブザーバーに通知します。もちろんLiveData単体での利用も可能です。

たとえば、MutableLiveData の使用方法は次のとおりです。

private void liveDataTest(){

    MutableLiveData<String> mutableLiveData = new MutableLiveData<>();

    mutableLiveData.observe (this, new Observer<String>() {

        @オーバーライド

        public void onChanged(文字列値) {

           //データが変更された場合、ここで通知が受信され、インターフェースの更新などのいくつかの反応をここで実行できます。

        }

     });

    mutableLiveData.setValue("値が変更されました");

}

3 つのステップに分かれています。

①データの種類に応じたLiveDataを作成する

②オブザーバーを追加してLifecycleとの接続を確立します

③該当データを送信して更新する

 

2. LiveDataの利用

①まず依存関係を導入する

実装「androidx.lifecycle:lifecycle-extensions:2.2.0」

②レイアウトファイル activity_live_data_test.xml

<?xml バージョン="1.0" エンコーディング="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="垂直">

    <TextView

        android:id="@+id/tv_text"

        android:layout_width="match_parent"

        android:layout_height="50dp"/>

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="ラップコンテンツ"

        android:orientation="水平">

        <画像ボタン

            android:id="@+id/img_button_add"

            android:layout_width="70dp"

            android:layout_height="70dp"/>

        <画像ボタン

           android:id="@+id/img_button_subtract"

            android:layout_width="70dp"

            android:layout_height="70dp" />

    </LinearLayout>

</LinearLayout>

③ ViewModelを継承したViewModelWithLiveDataクラスを作成し、このクラス内で変数を宣言する

public class ViewModelWithLiveData extends ViewModel {

    private MutableLiveData<Integer> LikedNumber;  

    public MutableLiveData<Integer> getLikedNumber() {

        if (いいね数 == null) {

            //LikedNumber はオブジェクト型であり、基本データ型ではないため、変数が空でないことを確認してください

            LikedNumber = new MutableLiveData<>();       

            LikedNumber.setValue(0); //0に初期化する

        }

        LikedNumber を返します。

    }

    public void addLikedNumber(int n) {

        LikedNumber.setValue( LikedNumber.getValue() + n);

    }

}

④ viewModelWithLiveData の変数に観測を追加し、ボタンをクリックして +1、-1 演算を実現します

public class LiveDataTestActivity extends AppCompatActivity {

    プライベート ViewModelWithLiveData viewModelWithLiveData;

    プライベート TextView tv_text;

    プライベートイメージボタン img_button_add;

    プライベート ImageButton img_button_subtract;

    @オーバーライド

    protected void onCreate(バンドル保存インスタンス状態) {

        super.onCreate(savedInstanceState);

        setContentView( R.layout.activity_live);

        viewModelWithLiveData = new ViewModelProvider(this).get(ViewModelWithLiveData.class);

        // viewModelWithLiveData の変数に観測値を追加し、それ自体を観測し、データが変更された場合は次の onChange 関数を呼び出します。observe() の最初のパラメータは、Activity などの LifeCycle 管理機能を備えたオブジェクトである必要があります。

        viewModelWithLiveData.getLikedNumber er().observe(this, new Observer<Integer>() {

          @オーバーライド

          public void onChanged(Integer integer) { //LiveData データが変更されたときにこの関数をコールバックします

              tv_text.setText( String.valueOf(integer));

            }

        });

        img_button_add.setOnClickListener(new View.OnClickListener() {

            @オーバーライド

            public void onClick(View view) {

                viewModelWithLiveData.addLikedNumber(1);

            }

        });

        img_button_subtract.setOnClickListener( new View.OnClickListener() {

            @オーバーライド

            public void onClick(View view) {

                viewModelWithLiveData.addLikedNumber(-1);

            }

        });

    }

}

ac6e22946cbb422a94eec0ed51489137.gif

また、画面を回転したり、システム言語やその他の設定を切り替えたりしても、データが失われることはありません。

 

LiveData の利点:

① バインドされているインターフェースのライフサイクルを監視します。したがって、LiveData を使用する場合、ライフ サイクルを手動で管理する必要はありません。

② コンポーネントは LiveData のデータ変更に適時に対応でき、常に最新の LiveData データを取得できます。もちろん、バインドされたコンポーネントが LiveData に応答するには、一定の前提条件があります。つまり、LiveData データが変更され、コンポーネントがアクティブであることです。つまり、LiveData データが変更された場合でも、必ずしも onChanged 関数に応答するとは限りません。これは、onChanged 関数に応答する前に、LiveData データが配置されているインターフェイスがアクティブである必要があるためです。

③ライフサイクルのバインドが自動的に解除されるため、コンポーネントが破棄されると自動的にバインドが解除され、アプリケーションでのメモリリークの可能性が大幅に減少します。

注: LiveData は確かにメモリ リークの問題を解決できますが、不適切に使用すると、依然としてメモリ リークが発生します。たとえば、Activity があり、Activity には Fragment が含まれており、Fragment には LiveData があるため、Fragment は LiveData を参照します。次に、LiveData は、observ メソッドを通じて、Activity を所有者としてバインドします。このとき、LiveData のライフ サイクルは、Activity のライフ サイクルと同じになります。このとき何らかの理由でFragmentが破壊されてもLiveDataはActivityから参照されているため破壊されません。LiveData をリサイクルする必要があるのにリサイクルできない場合、LiveData にメモリ リークが発生します。

 

3.LiveData ソースコード

①観察方法

LiveData は、observe() メソッドを使用して、それにバインドされているインターフェイスのライフサイクルを監視します。

LiveData.java:

@メインスレッド

public void observe(LifecycleOwner オーナー, Observer<? super T> オブザーバー) {

    assertMainThread("observe"); //このメソッドはメインスレッドでのみ呼び出すことができます

    if (owner.getLifecycle().getCurrentState() == 破壊) {

        return; // 所有者のライフサイクルがすでに DESTROYED 状態にある場合、それ以上の実行は行われません

    }

    // 外部から渡されたオブザーバー オブジェクトを LifecycleBoundObserver オブジェクトにカプセル化します。

    LifecycleBoundObserver ラッパー = 新しい LifecycleBoundObserver(所有者、オブザーバー);

    // オブザーバーとラッパーをそれぞれキーと値としてマップに格納します

    ObserverWrapper 存在 = mObservers.putIfAbsent(observer, ラッパー);

    if (existing != null && !existing.isAttachedTo(owner)) {

        throw new IllegalArgumentException( "異なるライフサイクルを持つ同じオブザーバーを追加することはできません"); //同じオブザーバーは異なる LifecycleOwner を監視できません

    }

    if (既存 != null) {

        戻る;

    }

    owner.getLifecycle().addObserver(wrapper);

}

まず、外部から渡されたオブザーバー オブジェクトを LifecycleBoundObserver オブジェクトにカプセル化します。オブジェクト名は Wrapper です。実際、LifecycleBoundObserver はライフサイクルを監視できるクラスです。LifecycleBoundObserver クラスの構築メソッドを確認できます。

LiveData.LifecycleBoundObserver.java:

class LifecycleBoundObserver extends ObserverWrapperimplemented GenericLifecycleObserver {

    最終的なライフサイクル所有者 mOwner;

    LifecycleBoundObserver(LifecycleOwner オーナー、Observer<? super T> オブザーバー) {

        スーパー(オブザーバー);

        mOwner = 所有者;

    }

    // …………………………

}

パブリック インターフェイス GenericLifecycleObserver extends LifecycleEventObserver { }

パブリックインターフェイス LifecycleEventObserver extends LifecycleObserver {

    void onStateChanged(LifecycleOwner ソース、Lifecycle.Event イベント);

}

LifecycleBoundObserver が GenericLifecycleObserver インターフェースを実装していることがわかりますが、実際には、このインターフェースは最終的にインターフェースを継承しますLifecycleObserverLifecycleObserverこれはlifecycle、使用時に addObserver を呼び出すときに渡す必要があるパラメーターです。したがって、新しい LifecycleBoundObserver(owner, Observer) メソッドによって取得されるラッパーは、実際には LifecycleObserver です。

LiveData の Observer メソッドに戻ると、コード行 mObservers.putIfAbsent(observer, Wrapper) が後で呼び出され、オブザーバーがキーとして使用され、ラッパーが値として mObservers に格納され、ObserverWrapper オブジェクトが次のようになります。戻ってきた。putIfAbsent メソッドは、値がすでに存在するかどうかを判断して戻り、そうでない場合は null を返します。したがって、null が返された場合は、オブザーバーが以前に追加されていないことを意味するため、LifecycleBoundObserver が所有者のライフサイクルのオブザーバーとして使用されます。

mObservers は、キーと値のペアのストレージをサポートするリンク リストによって実装されたデータ構造です。同時に、走査プロセス中の要素の削除もサポートしています。当面はオブザーバー オブジェクトのコレクションとして理解できます。

ObserverWrapper が null でない場合、observer メソッドは直接戻ります。どのような状況でObserverWrapperがnullではないのでしょうか?

オブザーバー Observer がすでに mObservers リストに含まれており、オブザーバーに別の所有者がすでに存在する場合、それは null にはなりません。

これは、同じオブザーバーが複数の LifecycleOwner にバインドされるのを防ぐためです。つまり、1 つの LiveData 内の同じオブザーバーは 1 つの LifecycleOwner にのみバインドできます。それ以外の場合、「異なるライフサイクルを持つ同じオブザーバーを追加することはできません」という例外がスローされます。

Observ メソッドの最後で、owner.getLifecycle().addObserver(wrapper) メソッドが呼び出され、ラッパーと所有者のライフサイクルがバインドされます。つまり、オブザーバーは、このときのオーナーに対応するインターフェースのライフサイクルの変化を監視することができる。

②setValueメソッド

setValue メソッドは LiveData データを更新するために使用され、データが更新されるとオブザーバーに通知されます。

LiveData.java:

@メインスレッド

protected void setValue(T 値) {

    assertMainThread("setValue"); //postValueが他のスレッドで使用される場合、メインスレッドで呼び出す必要があります

    mVersion++; データが更新されたかどうかの判断を支援します

    mData = 値;

    ディスパッチング値(null);

}

まず、assertMainThread を呼び出してメイン スレッド内にあるかどうかを確認し、次にその値を mData 変数に割り当て、mVersion 変数に 1 を追加します。mVersion はデータのバージョンであり、データが変更されたかどうかをマークするために使用されます。最後に、dispatchingValue() メソッドを呼び出して null を渡し、データを各オブザーバーに配布します。

メソッドに:

LiveData.java:

voiddispatchingValue(ObserverWrapperイニシエーター) {

    //データ配信中の場合、繰り返し処理は行われません

    if (mDispatchingValue) {

        mDispatchInvalidated = true;

        戻る;

    }

    mDispatchingValue = true; //イベント配布ステータスを true としてマークします

    する {

        mDispatchInvalidated = false;

        if (イニシエーター != null) {

            //observメソッドが呼び出されたら、ここからディストリビューションをトリガーします

            thinkNotify(イニシエーター);

            イニシエーター = null;

        } それ以外 {

            //setValue は、LiveData のすべてのオブザーバーを観察するためのトラバーサルをトリガーし、setValue イベントを配布します

            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {

                thinkNotify( iterator.next().getValue());

                if (mDispatchInvalidated) {

                    壊す;

                }

            }

        }

    while (mDispatchInvalidated);

    mDispatchingValue = false;

}

dispatchingValue メソッド パラメーターで null を渡す場合と null を渡さない場合の違いは、null が渡される場合はすべてのオブザーバーに通知され、それ以外の場合は受信オブザーバーのみに通知されることがわかります。

dispatchingValue メソッドには while ループがあります。while ループでは、パラメーターのイニシエーターが null であるため、すべての else ブランチは mObserver を走査することによってすべてのオブザーバーに通知し、すべての ObserverWrapper (実際には前の LifecycleBoundObserver) を取得し、オブザーバーに通知します。これは、通知の具体的な実装である thinkNotify メソッドを呼び出します。

このメソッドは、各オブザーバーを処理して、配布イベントを配布して完了する必要があるかどうかを判断します。

LiveData.java:

private void thinkNotify(ObserverWrapper オブザーバー) {

    //オブザーバーがアクティブでない場合、配布は中止されます。オブザーバーがアクティブになるロジックは STARTED と RESUMED の間です。

    if (!observer.mActive) {

        戻る;

    }

    if (!observer. shouldBeActive()) {

        オブザーバー.activeStateChanged(false);

        戻る;

    }

    // オブザーバーがすでにデータを受け入れている場合、それ以降は配布されません

    if (observer.mLastVersion >= mVersion) {

        戻る;

    }

    observer.mLastVersion = mVersion; //オブザーバーの配布バージョンを記録します

    observer.mObserver.onChanged((T) mData); // オブザーバーの onChanged イベントを呼び出して配布を完了します

}

まず、外部から渡されたオブザーバオブジェクトがアクティブ状態であるかどうかを判定し、アクティブ状態でない場合には、このメソッドを直接リターンする。ここから、データが更新されても、インターフェイスがアクティブでない場合、データの更新に応答しないことがわかります。

次に、mVersion 変数と mLastVersion 変数があります。observer.mLastVersion 変数が mVersion より小さい場合、mVersion の値が observer.mLastVersion 変数に代入されます。この場合、データは新しいデータであり、onChanged メソッドはオブザーバーの関数を実行して、mData を変換して呼び出すことができます。

③postValueメソッド

LiveData.java:

protected void postValue(T 値) {

    ブール型ポストタスク;

    同期済み (mDataLock) {

        postTask = mPendingData == NOT_SET;

        mPendingData = 値;

    }

    if (!postTask) {

        戻る;

    }

    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);

}

postValue メソッドでは、外部から渡された値が mPendingData に代入され、メソッドの最後に postToMainThread メソッド postToMainThread がこのメソッドの名前からわかるはずですが、これはこのランナブルをメインに公開することを意味します。処理するスレッドを指定し、postToMainThread と入力して詳細を確認してください。

ArchTaskExecutor.java:

@オーバーライド

public void postToMainThread(Runnable 実行可能) {

    mDelegate.postToMainThread(実行可能);

}

mDelegate の postToMainThread メソッドを呼び出します。mDelegate は実際には DefaultTaskExecutor クラスです。次に、このクラスの postToMainThread を入力して次を確認します。

DefaultTaskExecutor.java:

@オーバーライド

public void postToMainThread(Runnable 実行可能) {

    if (mMainHandler == null) {

        同期済み (mLock) {

            if (mMainHandler == null) {

                mMainHandler = 新しいハンドラー(Looper.getMainLooper());

            }

        }

    }

    mMainHandler.post(実行可能);

}

このメソッドでは、まず mMainHandler オブジェクトが null かどうかを確認し、null の場合はオブジェクトを作成します。このオブジェクトは Looper.getMainLooper() パラメータを通じて作成され、この mMainHandler がメイン スレッドのハンドラ オブジェクトであることを示していることに注意してください。

最後に、mMainHandler.post(runnable) メソッドを呼び出し、実行可能オブジェクトをメイン スレッドのハンドラーに渡して処理します。

では、ランナブルは何を実行するのでしょうか?

postValue メソッドに戻り、runnable が mPostValueRunnable であることを確認します。mPostValueRunnable がどのように定義されているかを確認してください。

LiveData.java:

private Final Runnable mPostValueRunnable = new Runnable() {

    @オーバーライド

    public void run() {

        オブジェクト newValue;

        同期済み (mDataLock) {

            newValue = mPendingData;

            mPendingData = NOT_SET;

        }

        setValue((T) newValue);

    }

};

run メソッドを参照して、最初に newValue 変数を定義し、次にロックを追加して newValue を mPendingData 変数に割り当てます。mPendingData は前に見た postValue メソッドの 3 行目に割り当てられ、mPendingData を NOT_SET に設定します。最後に setValue メソッドを呼び出します。

したがって、postValue メソッドは実際にスレッドをメインスレッドに切断して setValue メソッドを処理します。

 

4. LiveData のライフサイクルへの対応

例: LiveData オブジェクトを監視するインターフェイスがありますが、インターフェイスが非アクティブな場合、LiveData データが更新されても、インターフェイスは LiveData の変更にすぐには応答しません (インターフェイスが非アクティブであるため)。インターフェイスがアクティブ状態に戻ると、新しく更新された LiveData データがすぐに応答します。

では、インターフェイスがアクティブ状態に戻ると (たとえば、アクティビティが onPause 状態から onResume 状態に戻るとき)、更新されたばかりの LiveData データがすぐに応答するのはなぜでしょうか。

これは、LiveData の Observer メソッドでは、owner.getLifecycle().addObserver(wrapper) メソッドを通じて、ラッパーとオーナーのライフ サイクルがバインドされる、つまりオーナーのライフ サイクルが変更されるためです。ラッパーは onStateChanged. メソッドで監視されます。

所有者がアクティビティであると仮定すると、アクティビティが非アクティブな状態にあるときに、LiveData に対して setValue 操作が実行されると、それが thinkNotify メソッドの先頭で直接返され、observer.mObserver.onChanged メソッドは返されません。 LiveDataのデータ変更。

アクティビティがアクティブ状態に戻ると、ラッパー (つまり、LifecycleBoundObserver) の onStateChanged メソッドがコールバックされます。

LiveData.LifecycleBoundObserver类:

@オーバーライド

public void onStateChanged(LifecycleOwner ソース、Lifecycle.Event イベント) {

    if (mOwner.getLifecycle().getCurrentState() == 破壊) {

        削除オブザーバー(mObserver);

        戻る;

    }

    activeStateChanged( shouldBeActive());

}

アクティビティがアクティブ状態にある場合、onStateChanged の activeStateChanged メソッドが実行されます。

void activeStateChanged(boolean newActive){

    if (newActive == mActive) {

        戻る;

    }

    mActive = 新しいアクティブ;

    ブール値はInactive = LiveData.this.mActiveCount == 0;

    LiveData.this.mActiveCount += mActive ? 1 : -1;

    if (wasInactive && mActive) {

        onActive();

    }

    if (LiveData.this.mActiveCount == 0 && !mActive) {

        onInactive();

    }

    //アクティブ状態が処理されたら、dispatchingValue(this)メソッドを実行する

    if (mActive) {

        ディスパッチング値(this);

    }

}

まず、ここでは mActive に値が割り当てられていることがわかります。そして、最後の3行を見て、アクティブであればdispatchingValue(this)メソッドを実行し、この時点でsetValueの処理に戻ります。つまり、Activityライフサイクルがアクティブになった後、再度LiveDataをトリガーできます。の値が更新されます。

 

5. LiveData の自動アンバインド

LiveData は、バインドされているコンポーネントのバインドを自動的に解除できるため、メモリ リークの可能性を大幅に減らすことができます。これはどのように機能するのでしょうか? 引き続き、内部クラス LifecycleBoundObserver が表示されます。

LiveData.java:

class LifecycleBoundObserver extends ObserverWrapperimplemented GenericLifecycleObserver {

    最終的なライフサイクル所有者 mOwner;

    LifecycleBoundObserver(LifecycleOwner オーナー、Observer<? super T> オブザーバー) {

        スーパー(オブザーバー);

        mOwner = 所有者;

    }

    @オーバーライド

    public void onStateChanged(LifecycleOwner ソース, Lifecycle.Event イベント){

        if (mOwner.getLifecycle().getCurrentState() == 破壊) {

            削除オブザーバー(mObserver);

            戻る;

        }

        activeStateChanged( shouldBeActive());

    }

    @オーバーライド

    boolean isAttachedTo(LifecycleOwner オーナー) {

        mOwner == 所有者を返します。

    }

    @オーバーライド

    void detachObserver() {

        mOwner.getLifecycle().removeObserver(this);

    }

}

onStateChangedメソッドにおいて、オーナーのライフがDESTROYEDであることが検出された場合、LiveDataクラスのremoveObserverメソッドが実行され、その実装は以下のようになります。

@メインスレッド

public void RemoveObserver(final Observer<? super T> オブザーバー) {

    assertMainThread("removeObserver");

    ObserverWrapper が削除されました = mObservers.remove(observer);

    if (削除 == null) {

        戻る;

    }

    //LifecycleBoundObserver の detachObserver メソッドを実行する

    削除されました.detachObserver();

    //ObserverWrapperのactiveStateChangedメソッドを実行する

    削除されました。activeStateChanged(false);

}

RemoveObserver メソッドの主なプロセスは、次の 3 つのステップに分かれています。

① まず、現在のオブザーバー オブジェクトを mObservers コレクションから削除します。

②removed.detachObserver() を実行します。このコード行は実際に LifecycleBoundObserver の detachObserver メソッドを実行します。ここで mOwner と mObserver が実際にバインドされていません。

③ Removed.activeStateChanged(false) を実行すると、このコード行は実際に ObserverWrapper の activeStateChanged メソッドを実行し、false を渡します。

 

6. LiveDataのmData

setValue メソッドでは、値が LiveData の mData メンバー変数に割り当てられます。この mData を見てください。

プライベート揮発性オブジェクト mData = NOT_SET;

Java 同時実行では、変数の可視性を確保し、同時実行下で同じ変数に対する複数のスレッドによってデータの不整合が変更されるのを防ぐために、volatile が使用されます。

このことから、LiveData の mData はスレッドセーフであることがわかります。

 

LiveData 内のデータへの変更:

①Transformations.map()

LiveData オブジェクトに保存されている値をオブザーバーにディスパッチする前に変更したい場合は、Transformations.map() を使用できます。

public class MainActivity extends AppCompatActivity {

    @オーバーライド

    protected void onCreate(バンドル保存インスタンス状態) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        MutableLiveData<String> mutableLiveData = new MutableLiveData<>();

        mutableLiveData.observe(this, new Observer<String>() {

            @オーバーライド

            public void onChanged(final String s) {

                Log.d(TAG, "onChanged1:"+s);

            }

        });

        LiveData 変換されたLiveData =Transformations.map(mutableLiveData, new Function<String, Object>() {

            @オーバーライド

            public Object apply(文字列名) {

               名前 + "+Android 開発者" を返します。

            }

        });

        transformedLiveData.observe(this, new Observer() {

            @オーバーライド

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged2:"+o.toString());

            }

        });

        mutableLiveData.postValue("Android の最も強力な言語");

    }

}

Transformations.map() により、mutableLiveData に基づいて文字列が追加されます。

印刷結果:

D/MainActivity: onChanged1: Android の最も強力な言語

D/MainActivity: onChanged2: Android の最も強力な言語 + Android 開発者

②Transformations.switchMap()

いずれかのデータ変更を手動で制御および監視し、必要に応じていつでも監視を切り替えたい場合は、Transformations.switchMap() を使用できます。これは、switchMap() を除く Transformations.map() と似ています。 LiveData オブジェクトを返す必要があります。

public class MainActivity extends AppCompatActivity {

    MutableLiveData<String> mutableLiveData1;

    MutableLiveData<String> mutableLiveData2;

    MutableLiveData<Boolean> liveDataSwitch;

    @オーバーライド

    protected void onCreate(バンドル保存インスタンス状態) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mutableLiveData1 = 新しい MutableLiveData<>();

        mutableLiveData2 = 新しい MutableLiveData<>();

        liveDataSwitch = new MutableLiveData<Boolean>();

        LiveDatatransformedLiveData= Transformations.switchMap(liveDataSwitch, new Function<Boolean, LiveData<String>>() {

            @オーバーライド

            public LiveData<String> apply(Boolean input) {

                if (入力) {

                    mutableLiveData1 を返します。

                } それ以外 {

                    mutableLiveData2 を返します。

               }

            }

        });

        transformedLiveData.observe(this, new Observer<String>() {

            @オーバーライド

            public void onChanged(final String s) {

                Log.d(TAG, "onChanged:" + s);

            }

        });

        liveDataSwitch.postValue(false);//2

        mutableLiveData1.postValue("Android 開発者");

        mutableLiveData2.postValue("Android言語無敵");

    }

}

切り替えを制御する新しい MutableLiveData<Boolean>() を作成し、liveDataSwitch に割り当てます。liveDataSwitch の値が true の場合は、mutableLiveData1 を返し、それ以外の場合は、切り替え監視の目的を達成する mutableLiveData2 を返します。

liveDataSwitch.postValue(false) の場合、Android 言語の無敵状態を出力します。

liveDataSwitch.postValue(true) の場合、Android 開発者を出力します。

③複数のLiveDataデータソースを結合

MediatorLiveData は mutableLiveData を継承しており、複数の LiveData データ ソースを収集し、複数の LiveData データの変更を監視するコンポーネントの目的を達成できます。

public class MainActivity extends AppCompatActivity {

    @オーバーライド

    protected void onCreate(バンドル保存インスタンス状態) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        MutableLiveData<String> mutableLiveData1 = new MutableLiveData<>();

        MutableLiveData<String> mutableLiveData2 = new MutableLiveData<>();

        MediatorLiveData liveDataMerger = new MediatorLiveData<String>();

        liveDataMerger.addSource( mutableLiveData1, new Observer() {

            @オーバーライド

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged1:"+o.toString());

            }

        });

        liveDataMerger.addSource( mutableLiveData2, new Observer() {

            @オーバーライド

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged2:"+o.toString());

            }

        });

        liveDataMerger.observe(this, new Observer() {

            @オーバーライド

            public void onChanged(Object o) {

                Log.d(TAG, "onChanged:"+o.toString());

            }

        });

        mutableLiveData1.postValue("Android アドバンス ライト");

    }

}

MediatorLiveData の addSource を通じて 2 つの MutableLiveData をマージし、MutableLiveData データが変更されたときに MediatorLiveData がそれを認識できるようにします。

 

7.LiveData コンポーネント間通信

LiveData はデータ変更をコンポーネントにタイムリーに通知し、自動的にバインドを解除することもできますが、LiveData の用途はそれだけではありません。

EventBus を以前に使用したことがあるかどうかはわかりませんが、これは Android の従来のインテント、ハンドラー、ブロードキャスト、またはインターフェイス コールバックを置き換え、フラグメント、アクティビティ、およびサービス スレッド間でデータを転送し、メソッドを実行できます。EventBus の最大の特徴はシンプルさと分離です。そのフレームのアイデアはメッセージのパブリケーションとサブスクリプションであり、パブリッシャーとサブスクライバーを分離することで Android イベント配信を簡素化します。

実際、EventBus に似たフレームワークも LiveData を通じて実装でき、使用後に手動でバインドを解除する必要がなく、より便利でエレガントです。では、どうすればそれを達成できるのでしょうか?

まず、LiveData を静的に設定して、さまざまなアクティビティやフラグメントがこの変数にアクセスできるようにすることが考えられます。そのため、この変数の値の変化を監視することは問題ありません。この方法は確かにLiveDataのActivity/Fragment間の通信を実現することができます。

例: Activity1 と Activity2 という 2 つのインターフェイスがあるとします。Activity1 は LiveData 経由でデータを送信し、Activity2 でデータを受信します。次に、新しい MyStaticLiveData クラスを作成してパブリック LiveData を保存できます。

クラス MyStaticLiveData {

    コンパニオン オブジェクト {

        val liveData = MutableLiveData<String>()

    }

}

次に、Activity1 でデータを送信すると、Activity2 にジャンプするボタンが表示されます。

クラス Activity1 : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView( R.layout.activity_live_data)

        MyStaticLiveData.liveData.value = "LiveDataActivity からのデータ。"

    }

    楽しい toLiveData2Activity(view: View) {

        startActivity(Intent(this, Activity2::class.java))

    }

}

Activity2 では、Activity1 が送信したデータを受信できるようにパブリック LiveData と Activity2 をバインドして監視しています。onDestroy で LiveData は自動的にアンバインドされるため、アンバインドする必要はありません。

クラス Activity2 : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView( R.layout.activity_live_data2)

        MyStaticLiveData.liveData.observe(this, {

            tv2.text = それ

        })

    }

}

EventBus スティッキーイベントの機能がまさに実現されているようで、使い方がより簡潔になっています。

しかし、その欠点も明らかです. 十分にエレガントではありません. さらに、新しい LiveData を作成したい場合は、MyStaticLiveData に手動で作成する必要があります. さらに、これらの LiveData を統一的に管理するものはなく、それぞれの LiveDataこれらはすべて静的であるため、開発中にこの方法で記述することは推奨されません。これはデモンストレーションのみを目的としており、実際の開発シナリオでの使用は推奨されません。

さらに、開発プロセスでは、ほとんどの場合、スティッキー イベントは必要ありません。より多くのシナリオでは、リスナーが監視に登録した後に送信されたメッセージの受信を開始できるようにし、登録前に送信されたメッセージを受信しないようにしたいと考えています。

スティッキー イベントとは、オブザーバーが観察を開始する前に LiveData が変更されたとき、およびオブザーバーが観察を開始したときに発生します。オブザーバーには引き続き変更が通知されますが、これは厄介なイベントです。

 

8. データバックフィーディング

先ほど示した例では、業界ではデータ バックフローという別の一般的な名前があります。つまり、最初に LiveData の値が設定され、その後リスナーが LiveData の監視を開始しますが、このとき LiveData の値はすぐにリスナーにコールバックされます。

データのバックフロー、つまり setValue の後、このセットの値が複数回消費されることを観察します。例えば、2回目の観測で得られたデータは1回目の古いデータです。これは予期せぬ結果をもたらすでしょう。

データの逆流の理由はソース コードの観点から分析する必要があります。まず setValue メソッドを確認します。

LiveData.java:

静的最終整数 START_VERSION = -1;

private int mVersion = START_VERSION;

@メインスレッド

protected void setValue(T 値) {

    assertMainThread("setValue");

    mバージョン++;

    mData = 値;

    ディスパッチング値(null);

}

ここでの mVersion は setValue で自動的にインクリメントされることに注意してください。また、mVersion の値のデフォルトは -1 です。したがって、setValue が初めて呼び出されたとき、LiveData の mVersion の値は 0 と等しくなります。

観察のために LiveData を呼び出すと、観察は最終的に thinkNotify メソッドに進みます。

LiveData.java:

private void thinkNotify(ObserverWrapper オブザーバー) {

    if (observer.mLastVersion >= mVersion) {

        戻る;

    }

    Observer.mLastVersion = mVersion;

    observer.mObserver.onChanged((T) mData);

}

プライベート抽象クラス ObserverWrapper {

    ……

    int mLastVersion = START_VERSION;

    ……

}

オブザーバーの mLastVersion が mVersion より小さい場合、前のデータがリスナーにコールバックされることがわかります。さらに、オブザーバーの mLastVersion のデフォルト値も -1 であるため、considerNotify では、mLastVersion が mVersion より小さいことは明らかです (-1 < 0)。そのため、データをリスナーにコールバックするために onChanged メソッドが実行されます。 。

注: 観察対象のデータが変化したとき、その変化を観察者に通知する必要があるかどうかをどのように判断すればよいでしょうか? livedata ソース コードでは、このコントロールに使用される 2 つのバージョン番号があります。最初のバージョン番号は、オブザーバーに属するライブデータのバージョン番号であり、2 番目のバージョン番号は、ライブデータの各オブザーバーが lastVersion を持ち、これら 2 つのバージョン番号の値は、配布が成功するたびに同期されます。各オブザーバーの lastVersion をライブデータのバージョン番号に更新します。

データ逆流の例:例えば、画面を回転するとアクティビティが再構築されますが、アクティビティが異常破棄されて再構築された場合、ViewModelは破棄前のデータを保存し、アクティビティの再構築後にデータを復元します。したがって、LiveData メンバー変数の mVersion は、リビルド前の値に復元されます。ただし、Activity の再構築後は LiveData のobserve() メソッドが呼び出され、メソッド内に新しいインスタンスが作成され、mLastVersion が初期値に戻されます。LiveData 自体の特性により、Activity のライフサイクルが非アクティブからアクティブに変化すると、LiveData がイベント配信をトリガーし、画面回転後にデータが逆流します。

注: アクティビティ画面が回転しているときに ViewModel は破棄されないため、ViewModel はアクティビティのすべてのデータを保持している限り状態を復元できます。

9. データバックフィルソリューション

①mVersionを修正する。

これまでの分析により、LiveData がイベントを配信するかどうかを判断する鍵は、considerNotify メソッドにあることがわかります。

private void thinkNotify(ObserverWrapper オブザーバー) {

    if (!observer.mActive) {

        戻る;

    }

    if (!observer. shouldBeActive()) {

        オブザーバー.activeStateChanged(false);

        戻る;

    }

    if (observer.mLastVersion >= mVersion) {

        戻る;

    }

    Observer.mLastVersion = mVersion;

    //ノーインスペクションのチェックを外します

    observer.mObserver.onChanged((T) mData);

}

mLastVersion>=mVersion である限り、setValue または postValue を実行するたびに mVersion は +1 になり、以前に setValue または postValue が存在したことが証明されます。ここで、オブザーバーを呼び出す前に setValue メソッドが配布されないようにする必要があります。必要なのは、オブザーバーを呼び出す前にノードで mLastVersion = mVersion を変更することだけです。

ソース コードの検出を通じて、リフレクションを通じてオブザーバー内の mObservers オブジェクトと現在の mVersion を見つけることができ、ここで mVersion を mLastVersion に割り当てることができます。

private void フック (@NonNull Observer オブザーバー) が例外をスローする {

    //ラッパーのバージョンを取得する

    クラス classLiveData = LiveData.class;

    フィールド fieldObservers = classLiveData.getDeclaredField("mObservers");

    fieldObservers.setAccessible(true);

    オブジェクト objectObservers = fieldObservers.get(this);

    クラス> classObservers = objectObservers.getClass();

    メソッド methodGet = classObservers.getDeclaredMethod("get", Object.class);

    メソッドGet.setAccessible(true);

    オブジェクト objectWrapperEntry = methodGet.invoke(objectObservers, オブザーバー);

    オブジェクト objectWrapper = null;

    if (objectWrapperEntry インスタンスの Map.Entry) {

        objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();

    }

    if (objectWrapper == null) {

        throw new NullPointerException( "ラッパーは雄牛にはできません!");

    }

    クラス> classObserverWrapper = objectWrapper.getClass().getSuperclass();

    フィールド fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");

    フィールドLastVersion.setAccessible(true);

    //ライブデータのバージョンを取得する

    フィールド fieldVersion = classLiveData.getDeclaredField("mVersion");

    fieldVersion.setAccessible(true);

    オブジェクト objectVersion = fieldVersion.get(this);

    // ラッパーのバージョンを設定する

    fieldLastVersion.set(objectWrapper, objectVersion);

}

次に、LiveData を書き換えて、このフック メソッドを Observer メソッドに組み込みます。

このように、このカスタム LiveData を使用すると、最初に Value を設定してから観察する方法はもはや実行できないことがわかります、これがいわゆる非スティッキーです。

②利用SingleLiveEvent

SingleLiveEvent は、名前が示すように、更新を 1 回だけ送信する LiveData です。#LiveData を観察する場合、onChanged 操作は 1 つだけ応答できます。

そのコードは次のように実装されます。

クラス SingleLiveEvent<T> : MutableLiveData<T>() { 

    private val mPending = AtomicBoolean(false) 

    @メインスレッド

    override fun observe(owner: LifecycleOwner, Observer: Observer<in T>) {

        if (hasActiveObservers()) {

            Log.w("SingleLiveEvent", "複数のオブザーバーが登録されていますが、変更が通知されるのは 1 人だけです。")

        }

        super.observe(owner, { t ->

            if (mPending.compareAndSet(true, false)) {

                オブザーバー.onChanged(t)

            }

        })

    } 

    @メインスレッド

    オーバーライド楽しい setValue(t: T?) {

        mPending.set(true)

        super.setValue(t)

    }

    @メインスレッド

    楽しい呼び出し() {

        値 = null

    }

}

SingleLiveEvent の考え方は、onChanged がトリガーされるたびに、ブール値 mPending を使用して、最後の setValue イベントが消費されたかどうかを判断し、消費された場合、消費は引き継がれないということです。

ステップ 1: SingleLiveEvent は割り当てに AtomicBoolean (デフォルトは false) を使用し、LiveData が setValue を実行するときに AtomicBoolean の値を変更します (set(true))。

ステップ 2: AtomicBoolean.compareAndSet(true,false) メソッドを使用して、最初に判定 (このときの AtomicBoolean の値が true) して、compareAndSet で設定した例外値 (最初のパラメータ) と比較し、2 番目のパラメータを比較します。等しいです。AtomicBoolean の値を false に設定し、true を返します)。ここでは実際に AtomicBoolean のアトミック性を使用して比較と再割り当てのプロセスを実行しています。

ステップ 3: ページに再度入ると、LiveData 値は変更されていませんが、オブザーバー メソッドは依然としてトリガーされます。AtomicBoolean はすでに false ですが、excel 値は true であるため、onChanged(T) メソッドはトリガーされません。つまり、onChanged(T) メソッドは、setValue が設定されているときに 1 回だけ応答されます。

実際、SingleLiveEvent では「スティッキー」問題は解決されません。これが適用されるシナリオは、1 つの setValue の後に複数の観測値が存在するが、消費される観測値は 1 つだけであるということです。ただし、SingleLiveEvent の問題は、オブザーバーが 1 人に制限されていることです。誤って複数を追加した場合、呼び出されるのは 1 つだけであり、どれが呼び出されるかは保証されません。

③UnPeekLiveData

これは、KunMinX によってオープンソース化されたこのような問題の解決策です。

パブリック クラス ProtectedUnPeekLiveData extends LiveData {

    protected boolean isAllowNullValue;

    プライベート最終 HashMap オブザーバー = new HashMap();

    public voidobserveInActivity(@NonNull AppCompatActivity activity, @NonNull Observer super T>observer) {

        LifecycleOwner 所有者 = アクティビティ;

        Integer storeId = System.identityHashCode(observer);//ここでのソース コードは activity.getViewModelStore() であり、同じ ViewModel 環境内で「信頼できる一意のソース」を確保するためのものです。

        観察(ストアId、所有者、オブザーバー);

    }

    private void Observer(@NonNull Integer StoreId,@NonNull LifecycleOwner オーナー、@NonNull Observer super T>observer) {

        if (observers.get(storeId) == null) {

            observers.put(storeId, true);

        }

        super.observe(オーナー, t -> {

            if (!observers.get(storeId)) {

                observers.put(storeId, true);

                if (t != null || isAllowNullValue) {

                    オブザーバー.onChanged(t);

                }

            }

        });

    }

    @オーバーライド

    protected void setValue(T 値) {

        if (値 != null || isAllowNullValue) {

            for (Map.Entry エントリ :observers.entrySet()) {

                エントリ.setValue(false);

            }

            super.setValue(値);

        }

    }

    protected void clear() {

        super.setValue(null);

    }

}

このアイデアも非常に明確で、各受信オブザーバー オブジェクトのブール値を、オブジェクトが Observer メソッドに入ることができるかどうかのスイッチとして渡します。新しいオブザーバが保存されると、スイッチはデフォルトでオフになります。

各 setValue の後で、すべてのオブザーバー スイッチをオンにして、すべてのオブザーブが実行できるようにします。

同時に、メソッドが開始された後、現在実行中のオブザーバー スイッチを閉じます。つまり、Value をリセットしない限り、2 回目の実行はできません。

この仕組みにより、リフレクション技術を使わずにLiveDataの非スティッキーな状態を実現することができます。

 

 

おすすめ

転載: blog.csdn.net/zenmela2011/article/details/130068181