Google I/O 2017で追加された、Android Architecture Componentsを触ってみた感想です
Components
大きく分けて4つあります。
- Lifecycle
- LiveData
- ViewModel
- Room
Roomはちょっと時間かかりそうなので別の機会で。(やるかは未定)
とりあえず、Room以外の3つを触ってみました。
Lifecycle
これが一番重要だと思います。LiveDataもViewModelもこれがあるからこそ実現できるように感じました。
ActivityやFragmentのライフサイクルイベントの監視や状態の管理ができるようになります。
簡単な使い方です。
まずはLifecycleObserverをimplementsして、監視したいライフサイクルイベントをアノテーションで指定します。
public class SampleObserver implements LifecycleObserver { private static final String TAG = SampleObserver.class.getSimpleName(); @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) void onCreate() { Log.d(TAG, "onCreate"); } @OnLifecycleEvent(Lifecycle.Event.ON_START) void onStart() { Log.d(TAG, "onStart"); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) void onResume() { Log.d(TAG, "onResume"); } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) void onPause() { Log.d(TAG, "onPause"); } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) void onStop() { Log.d(TAG, "onStart"); } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) void onDestroy() { Log.d(TAG, "onDestroy"); } }
Activity側です。いつもはAppCompatActivity
を継承してると思いますが、LifecycleActivity
ってのを継承するようにします。
public class MainActivity extends LifecycleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SampleObserver observer = new SampleObserver(); getLifecycle().addObserver(observer); } }
ポイントはgetLifecycle()
でLifecycleRegistry
を取得して、addObserver
で先程つくったObserverを追加してあげてます。
これで実行すると、Observerの各ライフサイクルイベントが呼ばれるのが確認できると思います。
ライフサイクルのイベントは2つ同時に指定できたり、引数でLifecycleOwnerとEventを受け取れたりもします。詳しくはドキュメントを見てください。
LiveData
変更通知してくれるデータです。RxJavaのPublishSubject
に似てます。
単純な使い方
まずは単純な使い方です。
通知イベントを発火する側を作ります。
public class Sample { private final MutableLiveData<String> message = new MutableLiveData<>(); public LiveData<String> getMessage() { return message; } public void doSomething() { message.setValue("Test"); } }
MutableLiveData
を定義して、それのsetValue
を呼んであげると通知が送られます。通知を受け取る側は公開されてるLiveData
のほうを監視するようにします。
で、その監視する側です。
public class MainActivity extends LifecycleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Sample sample = new Sample(); sample.getMessage().observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String message) { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } }); } }
observe
メソッドで監視しています。第一引数がLifecycleOwner
で、第二引数が通知を受け取るObserverになっています。
これの嬉しいところは、RxJavaであったようにunsubscribeの処理を自分で書く必要がありません。onDestoryが呼ばれるときに自動で削除してくれます。なので、メモリーリークする心配がありません。
データを加工する
Transformationsを使うと、データを加工することができます。
先程の例に少し手を加えます。map
メソッドで大文字に変換しています。
public class Sample { private final MutableLiveData<String> message = new MutableLiveData<>(); public LiveData<String> getMessage() { return Transformations.map(message, new Function<String, String>() { @Override public String apply(String input) { // 大文字に変換 return input.toUpperCase(); } }); } public void doSomething() { message.setValue("Test"); } }
もう一つが、switchMap
ってのがあります。これうまく説明できないのですが、LiveDataの値が変更されたときに、他のLiveDataを返すことができます。
public class Sample { private final MutableLiveData<String> message = new MutableLiveData<>(); public LiveData<String> getMessage() { return Transformations.switchMap(message, new Function<String, LiveData<String>>() { @Override public LiveData<String> apply(String input) { // 別のLiveDataを返す return getOther(input); } }); } private LiveData<String> getOther(String input) { return new LiveData<String>() { @Override protected void onActive() { // Activeになったらすぐに通知する setValue(input.toLowerCase()); } }; } public void doSomething() { message.setValue("Test"); } }
あまり例がよくないですが、messageの変更あったときに、getOtherっていうメソッドを経由して別のLiveDataを返すようにしています。
このあたりのほうが参考になるかもしれません。
LiveDataを継承
単純な良い例が思い浮かばかなかったので、省略。
ドキュメントにある、LocationManagerの例が分かりやすいのでそっちを見てください。
ViewModel
最後にViewModelです。
MVVMの文脈のViewModelとは大きく異るというか、実態は空っぽのonCleared
だけが定義されてるabstractのクラスです。なのでMVVMアーキテクチャに対しては何もしてくれません。使い方次第です。
これは、回転とかでActivityが死んだ時にもデータを消えずに残してくことができます。
ぼくはMVVMを使っててよくViewModel(MVVM)の情報をonSaveInstanceState
で保持して、onCreate
で復元するってのをよくやるんですけど、これが不要になってきます。
ドキュメントの下の図を見てもらえると生存期間が分かると思います。
単純な使い方
まずは単純な使い方です。
ViewModelを継承してonCleared
をオーバーライドしただけのクラスです。
public class MainViewModel extends ViewModel { private static final String TAG = MainViewModel.class.getSimpleName(); @Override protected void onCleared() { Log.d(TAG, "onCleared"); } }
次にActivity側です。ViewModelProviders
経由でViewModelのインスタンスを生成してあげることで、ライフサイクルの管理を任せられます。自分でnewしても意味はないので注意が必要です。
public class MainActivity extends LifecycleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class); } }
Contextがほしいとき
ViewModel内でContextがほしいときはAndroidViewModel
を継承すると、コンストラクタで渡してもらえます。
コンストラクタに引数を渡したい
ViewModelに引数を渡したい場合はFactoryを作る必要があります。
先程のViewModel内にViewModelProvider.NewInstanceFactory
を継承したFactoryクラスを作ります。
そのFactoryでインスタンスを生成するようにします。
public class MainViewModel extends ViewModel { private static final String TAG = MainViewModel.class.getSimpleName(); public MainViewModel(UserRepository userRepository) { // ... } @Override protected void onCleared() { Log.d(TAG, "onCleared"); } public static class Factory extends ViewModelProvider.NewInstanceFactory { private UserRepository userRepository; public Factory(UserRepository userRepository) { this.userRepository = userRepository; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { //noinspection unchecked return (T) new MainViewModel(userRepository); } } }
Activity側は作ったFactoryを使うように指定してあげます。
public class MainActivity extends LifecycleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MainViewModel viewModel = ViewModelProviders .of(this, new MainViewModel.Factory(new UserRepository())) .get(MainViewModel.class); } }
まとめ
まだまだ、alph版なので変更はあると思いますが、ざっと触ってみた感じすごく期待できそうでした。
ぼくはMVVMで通知の仕組みをRxJavaのSubjectで実現させてるのですが、LiveDataで置き換えられるなぁって思ってます。
あとは、LifecycleのおかげでRxLifecycleもいらなくなってくる感じですかね。
おまけ
ぼんやりドキュメント眺めてたら、面白そうなクラスがありました。まだ実装は無いようです。
どうなるか楽しみです。