うさがにっき

読書感想文とプログラムのこと書いてきます

Retrolamda, RxAndroidを使ったJava8っぽいAndroidコーディングはへっぽこアンドロイダーにも恩恵をもたらすのか?(RxAndroid編)

詳細

RxAndroidとは?

RxAndroid READMEより


RxAndroid: Reactive Extensions for Android

Android specific bindings for RxJava.

This module adds a number of classes to RxJava that make writing reactive components in Android applications easy and hassle free.
More specifically, it

provides a Scheduler that schedules an Observable on a given Android Handler thread, particularly the main UI thread
provides base Observer implementations that make guarantees w.r.t. to reliable and thread-safe use throughout Fragment and Activity life-cycle callbacks (coming soon)
provides reusable, self-contained reactive components for common Android use cases and UI concerns (coming soon)

RxAndroidはAndroidのためのRXである

RxJavaをAndroidのためにバインドしたものである

このモジュールはRxJavaにいくつかのクラスを加えたもので、Androidアプリケーションにreactive要素を持ち込むことが簡単である
具体的には

  • Android Handler thread, 特にUI threadをObservableのSchedulerにできる
  • base ObserverにFragment, Activityライフサイクルのコールバックに対してw.r.t[信頼できるスレッドセーフ]を保証できる(まだ未実装だよ)
  • 再利用性を提供する、共通のAndroid use case と UI の懸念へ、自己完結したreactive componentsを提供する(まだ未実装だよ)

未実装の機能も多い
GitHubを観察する限りまだまだ活発な議論が行われており、製品に利用するには慎重にならなければならない箇所も多そう

どう使うの?

build.gradleに以下を追加

compile 'io.reactivex:rxandroid:0.24.0'

何に使えるの?

Adapter内のlist要素操作に使える

これは前回のブログ記事と被るけど、Adapter内のデータに対するリッチなIteratorとして使うことができる
例えば下記のようなObservableをAdapterから生やしておく

public class XXXAdapter extends ArrayAdapter<Todo> {
  public Observable<Todo> items() {
    return Observable
      .range(0, getCount())
      .map(i -> getItem())
      .window(getCount())
      .toBlocking()
      .single();
  }
}

するとAdapterの利用側から以下のような操作ができる

adapter.items()
  // チェックのついたものだけを取得
  .filter((Todo todo) -> todo::isChecked)
  // チェックのついたものをadapterから削除
  .subscribe(adapter::remove)

このようにAdapter内に取得したいObservableを生やしておくことによりAdapterの利用側からAdapter内のデータを簡単に利用できる

通信処理のInterfaceの一元化

これも前回のブログと被るけど、非同期通信処理のインタフェースをあわせることができる
エラーハンドリングや実装のインタフェースがずれがちなAsynkTaskより効率的に管理できる

非同期処理を行うObservableを作成する

public Observable<Todo> getTodo(final long todoId) {
  return Observable.create((Subscriber<? super Todo> subscriber) -> 
    try {
      // 非同期通信処理
      Todo todo = loadTodo(todoId);
      subscriber.onNext(todoId);
      subscriber.onComplete();
    }catch(Exception e) {
      subscriber.onError(e);
    }
  );
}

非同期処理を実行したいクラスで、Observableを作成し、どのスレッドで実行するか設定

Observable<Todo> todoObservable = getTodo(id);

todoObservable
  // Observableの実行スレッド
  .subScribeOn(Scheduler.io())
  // Observerの実行スレッド
  .observeOn(AndroidSchedulers.mainThread());

非同期処理を実行

todoObservable.subscribe(
  // complete
  @override
  public void onComplete()
    hideProgress();
  }

  // exception
  @override
  public void onError(Throwable e)
    showError(e);
  }

  // success
  @override
  public void onNext(Todo todo)
    showTodo(todo);
  }  
);
ある程度のイベントを処理するObservableが用意されている

例えばViewObservableクラスを使うことで以下のイベント処理できる
ViewObservable (RxJava Javadoc unspecified)

Buttonのクリックイベントを取得し、textViewにクリック回数を示すのは以下の感じになる

ViewObservable.clicks(findViewById(R.id.button))
  // 現在値設定
  .map((OnClickEvent clickEvent) -> { 
    return 1; 
  })
  // 前回の値に今の値を足す
  .scan((Integer acc, Integer current) -> {
    return acc + current; 
  })
  // 総合値をtextViewに表示
  .subscribe((Integer clickCount) -> {
    TextView textView = (TextView)findViewById(R.id.text_view);
    textView.setText(clickCount.toString());
  })

コールバックの登録、解除も簡単にできる
コールバックが帰ってきたときに、画面が終わっててクラッシュとかを防げる

CompositeSubscription (RxJava Javadoc 1.0.11)

  private CompositeSubscription subscriptions = new CompositeSubscription();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    subscriptions.add(
      ViewObservable.clicks(findViewById(R.id.button))
        // 現在値設定
        .map((OnClickEvent clickEvent) -> { 
          return 1; 
        })
        // 前回の値に今の値を足す
        .scan((Integer acc, Integer current) -> {
          return acc + current; 
        })
        // 総合値をtextViewに表示
        .subscribe((Integer clickCount) -> {
          TextView textView = (TextView)findViewById(R.id.text_view);
          textView.setText(clickCount.toString());
        })
    )
  }

  @Override
  public void onDestroy() {
      subscriptions.unsubscribe();
      super.onDestroy();
  }


次回はrxandroidをもっと掘り下げてみる
AndroidObservable (RxJava Javadoc unspecified)
とかライフサイクルに則ったObservableみたいなので見てみたい