Google I/O 2017 で Android Arcitecture Components が発表されました。
Android Arcitecture Components には、大きく分けて以下の4つの要素があります。
- Handling Lifecycles
- Live Data
- View Model
- Room Persistence Library
今回は、そのうちの Handling Lifecycle
, Live Data
, View Model
の3つを紹介したいと思います。
Handling Lifecycle
Handling Lifecycle とは、 Activity と Fragment の Lifecycle を監視し Lifecycle に基づいた処理を実装しやすくするためのものです。
例えば、アプリ( Activity )が生きている時のみ位置情報を取得しようとする時、以下のような実装になることが多かったと思います。
class MyLocationListener { public MyLocationListener(Context context, Callback callback) { // ... } void start() { // connect to system location service } void stop() { // disconnect from system location service } } class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } public void onStart() { super.onStart(); myLocationListener.start(); } public void onStop() { super.onStop(); myLocationListener.stop(); } }
このパターンは、 GPS サービスとの接続状態更新を Activity のライフサイクルのコールバックメソッドに直接書くことで実現しています。
接続状態の更新を必ず書かなければならないため、実装ミスなどの要因となるデメリットがあります。
LifecycleObserver の使用方法
このデメリットを回避するために Handling Lifecycle の LifecycleObserver というクラスを用いて、 MyLocationListener を以下のように変更します。
public class MyLocationListener implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_START) public void onStart() { // connect to system location service } @OnLifecycleEvent(Lifecycle.Event.ON_STOP) public void onStop() { // disconnect from system location service } }
この LifecycleObserver クラスは、@OnLifecycleEvent アノテーションを用いてライフサイクルの中で呼ばれる状態とメソッドをクラス内に定義することができます。
@OnLifecycleEvent で設定できるライフサイクルの状態は、以下が設定できます。
ライフサイクル | 指定する |
---|---|
すべて | Lifecycle.Event.ON_ANY |
onCreate | Lifecycle.Event.ON_CREATE |
onStart | Lifecycle.Event.ON_START |
onResume | Lifecycle.Event.ON_RESUME |
onPause | Lifecycle.Event.ON_PAUSE |
onStop | Lifecycle.Event.ON_STOP |
onDestroy | Lifecycle.Event.ON_DESTROY |
LifecycleObserver を実装したクラスは、 LifecycleActivity を継承した Activity の onCreate 内で MyLocationListener のインスタンスを以下のメソッドを用いて LifecycleOwner に登録します。
public void onCreate(Bundle savedInstanceState){ getLifecycle().addObserver(new MyLocationListener()); }
このように LifecycleObserver クラスを用いるとライフサイクルに基づいた処理の実装漏れを防ぐことができます。
Live Data
LiveData は、値を監視し値の変更通知を受けることができるクラスで、値の更新があった時のみ View の表示を更新するといったことが可能になります。
値の変更通知は、 LiveData が Active 状態の時のみ通知されます。
Active 状態というのは、 Activity が生きている状態を指し Activity#onStart から Activity#onStop までもしくは、 Activity#onResume から Activity#onPause までを指します。
LiveData は、実際に以下のように使用します。
public class TimeLiveData { private final MutableLiveData<String> timeLiveData = new MutableLiveData<String>(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy 年 MM 月 dd 日 E 曜日"); public LiveData<String> getLiveData() { return timeLiveData } public void update(){ Calendar c = Calendar.getInstance(); timeLiveData.setValue(sdf.format(c.getTime())) } } public class MainActivity extends LifecycleActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TimeLiveData time = new TimeLiveData(); time.getLiveData().observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String message) { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } }); // 定期実行 final Handler handler = new Handler(); final Runnable r = new Runnable() { int count = 0; @Override public void run() { time.update(); handler.postDelayed(this, 1000); } }; handler.post(r); } }
View Model
ViewModel は、 View のデータを保持する役割を持ち、 Activity 、 Fragment の画面構成の変更(端末の画面回転等)で再生成される際に自動的にデータを保持します。
従来、 View のデータを保持する場合 onSaveInstanceState()
を用いていました。
しかし、 onSavedInstanceState() では、大きなデータを保存することはできません。
また、メモリリークを防ぐためにデータリソースの解放を行う必要があったりと使い勝手の良いものではありませんでした。
ViewModel のライフサイクルは、 Activity や Fragment よりも長く Activity#onCreate から Activity#onDestroy までになります。
ゆえに、 View や ActivityContext などを ViewModel で参照してはいけません。
ViewModel は、以下のように使用します。
以下の例では LiveData と併用し、データの更新監視も行なっています。
public class UserListViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<Users>>(); loadUsers(); } return users; } private void loadUsers() { // do async operation to fetch users } }
public class UsersActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { UserListViewModel model = ViewModelProviders.of(this).get(UserListViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } }
また ViewModel では、 Fragment 間のデータの共有を行うこともできます。
ViewModel を使わずに Fragment 感でデータの共有を行う場合、 Activity への機能追加が多くなり Activity のコードがより複雑になってしまいます。
Fragment 間のデータの共有は、2 つの Fragment で同じ ViewModel にアクセスして行います。
これにより Fragment 間のデータの共有をするのに Activity に手を入れることはなくなります。
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData<Item> getSelected() { return selected; } } public class MasterFragment extends Fragment { private SharedViewModel model; public void onActivityCreated() { model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends LifecycleFragment { public void onActivityCreated() { SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); model.getSelected().observe(this, { item -> // update UI }); } }
1 つ注意しなければいけないのは、 ViewModelProviders.of で同じ Activity のインスタンスを使用することで、 Fragment 間でデータを共有できるのは同じ Activity に属する Fragment 間のみとなります。
まとめ
Android Architecture Component の登場で Activity や Fragment のライフサイクルを気にすることが減り、
Acitivity や Fragment の機能追加によるコードの複雑化も解消でき、
MVVM 設計の下、より明確にクラスと機能の分離を行うことができるようになりました。
Android Architecture Component は今後の Android アプリ開発においてなくてはならないものになるのではないでしょうか。
Android Architecture Component の 1 つである Room Persistence Library を別記事にまとめてあります。
よろしければこちらもお読みください。