Androidアーキテクチャコンポーネント。 パート3. LiveData

画像



コンポーネントLiveData-オブジェクトを保存するように設計されており、その変更をサブスクライブできます。 重要な機能は、コンポーネントがライフサイクルを認識し、サブスクライバーが現在どの段階にあるかを気にしないことです。サブスクライバーが破壊された場合、コンポーネントはそれ自体からサブスクリプションを解除します。 LiveDataがライフサイクルを考慮するために、ライフサイクルコンポーネントが使用されますが、ライフサイクルを参照せずに使用することもできます。



コンポーネント自体は、 LiveData、MutableLiveData、MediatorLiveData、LiveDataReactiveStreams、変換およびインターフェースのクラスObserverで構成されています。



LiveDataクラスは抽象ジェネリッククラスであり、コンポーネントのロジック全体をカプセル化します。 したがって、LiveDataホルダーを作成するには、このクラスを継承し、格納する予定のタイプをタイプとして指定し、格納されたオブジェクトの更新ロジックを記述する必要があります。



値を更新するには、setValue(T)メソッドを使用して値を渡す必要があります。このメソッドはメインスレッドから呼び出す必要があるため注意してください。そうしないと、 IllegalStateExceptionを取得します。メソッドは、メインスレッドの値を更新します。 postValue(T)の興味深い機能は、複数の呼び出しの場合、メインスレッドへの呼び出しのキューを作成せず、メインスレッドでコードを実行するときに、最後に受け取った値を取得することです。 また、クラスには2つのコールバックがあります。



onActive() -サブスクライバの数がその値を0から1に変更すると呼び出されます。

onInactive() -サブスクライバーの数がその値を1から0に変更すると呼び出されます。



その目的は、データを更新する必要があるかどうかをクラスに通知することです。 デフォルトでは実装されていません。これらのイベントを処理するには、これらのメソッドをオーバーライドする必要があります。



LiveDataクラスがどのように見えるかを見てみましょう。これは、妻のネットワーク名を保存し、変更された場合、それを更新して、シングルトンとして実装されるのを簡素化します。



public class NetworkLiveData extends LiveData<String> {   private Context context;   private BroadcastReceiver broadcastReceiver;   private static NetworkLiveData instance;   public static NetworkLiveData getInstance(Context context){       if (instance==null){           instance = new NetworkLiveData(context.getApplicationContext());       }       return instance;   }   private NetworkLiveData(Context context) {       this.context = context;   }   private void prepareReceiver(Context context) {       IntentFilter filter = new IntentFilter();       filter.addAction("android.net.wifi.supplicant.CONNECTION_CHANGE");       filter.addAction("android.net.wifi.STATE_CHANGE");       broadcastReceiver = new BroadcastReceiver() {           @Override           public void onReceive(Context context, Intent intent) {               WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);               WifiInfo wifiInfo = wifiMgr.getConnectionInfo();               String name = wifiInfo.getSSID();               if (name.isEmpty()) {                   setValue(null);               } else {                   setValue(name);               }           }       };       context.registerReceiver(broadcastReceiver, filter);   }   @Override   protected void onActive() {       prepareReceiver(context);   }   @Override   protected void onInactive() {       context.unregisterReceiver(broadcastReceiver);       broadcastReceiver = null;   } }
      
      





一般に、フラグメントのロジックは次のとおりです。誰かがサインアップすると、BroadcastReceiverを初期化し、ネットワークの変更を通知します。最後のサブスクライバーがサブスクライブを解除すると、ネットワークの変更の追跡を停止します。



サブスクライバーを追加するには、2つの方法があります。observe( LifecycleOwner、Observer <T>) -ライフサイクルを考慮してサブスクライバーを追加し、 observForever(Observer <T>) -除外します。 データ変更の通知は、1つのonChanged(T)メソッドを持つObserverインターフェースの実装を介して行われます。



次のようになります。



 public class MainActivity extends LifecycleActivity implements Observer<String> {  private TextView networkName;  @Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.activity_main);      networkName = (TextView) findViewById(R.id.network_name);      NetworkLiveData.getInstance(this).observe(this,this);      //NetworkLiveData.getInstance(this).observeForever(this);  }  @Override  public void onChanged(@Nullable String s) {      networkName.setText(s);  } }
      
      





注:このスニペットは参照専用であり、実際のプロジェクトではこのコードを使用しないでください。 LiveDataを使用するには、ViewModel(次の記事のこのコンポーネントについて)を使用するか、オブザーバーの手動でのサブスクライブ解除を行うことをお勧めします。

画面を回転するときにobserve(this、this)を使用すると、毎回コンポーネントのサブスクリプションが解除され、再署名されます。 observeForever(これ)を使用した場合、メモリリークが発生します。



上記のメソッドに加えて、LiveData apiにはgetValue()、hasActiveObservers()、hasObservers()、removeObserver(Observer <T>オブザーバー)、removeObservers(LifecycleOwner所有者)には追加のコメントは不要です。



MutableLiveDataクラスLiveDataの拡張ですが、抽象クラスではなく、 setValue (T)メソッドとpostValue(T)メソッドがAPI、つまりpublicに表示される点が異なります。

実際、このクラスは、値を更新するロジックをLiveDataに入れたくないが、ホルダーとしてのみ使用したい場合のヘルパーです。



 void update(String someText){      ourMutableLiveData.setValue(String); }
      
      





MediatorLiveDataクラスは、その名前が示すとおり、メディエーターパターンの実装です。念のため、念頭に置きます。動作パターンは、多くのオブジェクトが相互作用する方法をカプセル化するオブジェクトを定義し、相互に明示的に参照する必要を排除します。 クラス自体はMutableLiveDataを拡張し、2つのメソッドaddSource(LiveData <T>、Observer <T>)およびremoveSource(LiveData <T>)を APIに追加します。 クラスを操作する原則は、特定のソースではなくMediatorLiveDataにサブスクライブし、 addSource(..)を使用してソースを追加することです 。 MediatorLiveDataは、ソースへのサブスクリプションを管理します。



たとえば、モバイルネットワークの名前を保存する別のLiveDataクラスを作成しましょう。



 public class MobileNetworkLiveData extends LiveData<String> {  private static MobileNetworkLiveData instance;  private Context context;  private MobileNetworkLiveData(Context context) {      this.context = context;  }  private MobileNetworkLiveData() {  }  @Override  protected void onActive() {      TelephonyManager telephonyManager = (TelephonyManager) context              .getSystemService(Context.TELEPHONY_SERVICE);      String networkOperator = telephonyManager.getNetworkOperatorName();      setValue(networkOperator);  }  public static MobileNetworkLiveData getInstance(Context context) {      if (instance == null) {          instance = new MobileNetworkLiveData(context);      }      return instance;  } }
      
      





そして、wifiネットワークの名前を表示するようにアプリケーションを書き直し、wifiへの接続がない場合はモバイルネットワークの名前を表示します。このため、MainActivityを変更します。



 public class MainActivity extends LifecycleActivity implements Observer<String> {  private MediatorLiveData<String> mediatorLiveData;  private TextView networkName;  @Override  protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      setContentView(R.layout.activity_main);      networkName = (TextView) findViewById(R.id.network_name);      mediatorLiveData = new MediatorLiveData<>();      init();  }  private void init() {      final LiveData<String> network = NetworkLiveData.getInstance(this);      final LiveData<String> mobileNetwork = MobileNetworkLiveData.getInstance(this);      Observer<String> networkObserver = new Observer<String>() {          @Override          public void onChanged(@Nullable String s) {              if (!TextUtils.isEmpty(s))                  mediatorLiveData.setValue(s);              else                  mediatorLiveData.setValue(mobileNetwork.getValue());          }      };      Observer<String> mobileNetworkObserver = new Observer<String>() {          @Override          public void onChanged(@Nullable String s) {                  if (TextUtils.isEmpty(network.getValue())){                      mediatorLiveData.setValue(s);                  }          }      };      mediatorLiveData.addSource(network, networkObserver);      mediatorLiveData.addSource(mobileNetwork,mobileNetworkObserver);      mediatorLiveData.observe(this, this);  }  @Override  public void onChanged(@Nullable String s) {      networkName.setText(s);  } }
      
      





ご覧のとおり、UIはMediatorLiveDataにサブスクライブされ、特定のデータソースから抽象化されています。 メディエーターの値はソースに直接依存しないため、手動で設定する必要があることに注意してください。



LiveDataReactiveStreamsクラス (最初は私を誤解させました)は、RXを使用するLiveData拡張機能であると考えました。実際、このクラスは、2つの静的メソッドを持つアダプターです。fromPublisher(Publisher <T> (LifecycleOwnerライフサイクル、LiveData <T> liveData) 、Publisher <T>オブジェクトを返します。 このクラスを使用するには、個別にインポートする必要があります。

「android.arch.lifecycle:reactstreams:$ version」をコンパイルします



LiveDataの型を変更するためのヘルパーであるTransformationsクラスには、2つの静的メソッドがあります。

マップ(LiveData <T>、Function <T、P>) -メインスレッドのFunctionインターフェイスの実装を適用し、LiveData <P>オブジェクトを返します。Tは、着信LiveDataのタイプであり、Pは、実際、毎回発信LiveDataの希望のタイピングです着信LiveDataに変更があり、発信を通知します。また、Functionの実装を使用して型を変換した後、サブスクライバに通知します。 このメカニズム全体は、実際には発信LiveDataがMediatiorLiveDataであるという事実により機能します。



 LiveData<Location> location = ...; LiveData<String> locationString = Transformations.map(location, new Function<Location, String>() {  @Override  public String apply(Location input) {      return input.toString;  } });
      
      





switchMap(LiveData <T>、Function <T、LiveData <P >>) -mapメソッドに似ていますが、関数の型を変更する代わりに、形成されたLiveDataオブジェクトを返します。



 LiveData<Location> location = ...; LiveData<Place> getPlace(Location location) = ...; LiveData<Place> userName = Transformations.switchMap(location, new Function<Location, LiveData<Place>>() {  @Override  public LiveData<Place> apply(Location input) {      return getPlace(input);  } });
      
      





リポジトリで基本的な例を見ることができます: git



また、便利なリンク: oneおよびtwo



Androidアーキテクチャコンポーネント。 パート1.はじめに

Androidアーキテクチャコンポーネント。 パート2.ライフサイクル

Androidアーキテクチャコンポーネント。 パート3. LiveData

Androidアーキテクチャコンポーネント。 パート4. ViewModel



All Articles