ほげほげ(仮)

仮死状態

Android Architecture Components 感想

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, "onStop");
    }

    @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のPublishSubjectBehaviorSubjectに似てます。

単純な使い方

まずは単純な使い方です。

通知イベントを発火する側を作ります。

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もいらなくなってくる感じですかね。

おまけ

ぼんやりドキュメント眺めてたら、面白そうなクラスがありました。まだ実装は無いようです。

LiveDataReactiveStreams

どうなるか楽しみです。