Androidのリアクティブプログラミングの基本

1.リアクティブプログラミングの概要



多くのネットワーク接続、ユーザーインタラクション、アニメーションを備えた複雑なAndroidアプリケーションを開発するということは、ネストされたコールバックでいっぱいのコードを書くことを意味します。 また、プロジェクトの開発に伴い、このようなコードは扱いにくく理解しにくくなるだけでなく、開発、保守、および多くの微妙なエラーが発生しやすくなります。



ReactiveXまたは機能的なリアクティブプログラミングは、アプリケーションコードを大幅に削減し、非同期タスクとイベントを管理するためのエレガントでわかりやすいアプリケーションを作成できる代替アプローチを提供します。 リアクティブプログラミングでは、コンシューマはデータが到着すると応答し、イベントの変更を登録されたオブザーバに伝達します。



RxJavaは、JavaでのReactiveXのオープンソース実装です。 リアクティブコードの基本的な構成要素は、ObservablesとSubscribersです。 基本的なフレームワークの詳細については、 Grokai * RxJava、パート1:the basicsの記事を参照してください。



RxAndroidはRxJavaの拡張機能です。これにより、スケジューラーはAndroidアプリケーションのメインスレッドと追加のスレッドでコードを実行し、作成された追加のスレッドからメインスレッドに結果を転送して、ユーザーインターフェイスとの統合と対話を可能にします。

リアクティブプログラミングの基本原則をよりよく理解するために、Androidプラットフォームの実用的な例を検討してください。 そして、開発環境をセットアップすることから始めましょう。



2.環境の準備



メインライブラリを接続し、buil.gradle構成ファイルの依存関係{}セクションに依存関係を記述します。

dependencies { compile 'io.reactivex:rxandroid:1.2.1' compile 'io.reactivex:rxjava:1.1.6' }
      
      





ラムダ式のサポートを含む-Android NプラットフォームでJava 8言語の新機能を使用しますJava 8言語の機能を使用するには、build.gradleファイルに追加する新しいJackコンパイラも接続する必要があります。

 android { ... defaultConfig { ... jackOptions { enabled true } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
      
      





注: JackはAndroid Studio 2.1でのみサポートされており、JDK 8にアップグレードする必要もあります。



gradle構成ファイルに変更を加えると、プロジェクトを同期する必要があるという警告が表示されます。すべての変更を適用するには、右上の[今すぐ同期]リンクをクリックします。



3.基本的な例を作成する



ほとんどの場合、RxAndroidの使用は、ネットワーク接続のマルチスレッド処理を伴うプロジェクトに関連付けられているという事実のため、サイト解析の結果を処理する簡単な例を考えてください。

結果を表示するには、簡単なレイアウトを作成します。

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" ...> <ScrollView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/scrollView" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/textView" /> </ScrollView> </RelativeLayout>
      
      





解析のために、2つのメソッドgetURLsおよびgetTitleを使用して単純なWebParsingクラスを作成します。

 public class WebParsing { public List<String> getURLs(String url) { Document doc; List<String> stringList = new ArrayList<>(); try { doc = Jsoup.connect(url).get(); Elements select = doc.select("a"); for (Element element : select) { stringList.add(element.attr("href")); } } catch (IOException e) { e.printStackTrace(); return null; } return stringList; } }
      
      





 public String getTitle(String url) { String title; try { Document doc = Jsoup.connect(url).get(); title = doc.title(); } catch (MalformedURLException mue) { mue.printStackTrace(); return null; } catch (HttpStatusException hse) { hse.printStackTrace(); return null; } catch (IOException e) { e.printStackTrace(); return null; } catch (IllegalArgumentException iae) { iae.printStackTrace(); return null; } return title; }
      
      





getURLsメソッドは、サイトのコンテンツをスキャンし、見つかったすべてのリンクのリストを返します。getTitleメソッドは、リンクによってサイトのタイトルを返します。



4.反応性をつなぐ



上記の方法に基づいてRxAndroidの機能を使用するには、対応する2つのObservableを作成します。

 Observable<List<String>> queryURLs(String url) { WebParsing webParsing = new WebParsing(); return Observable.create( new Observable.OnSubscribe<List<String>>() { @Override public void call(Subscriber<? super List<String>> subscriber) { subscriber.onNext(webParsing.getURLs(url)); subscriber.onCompleted(); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); }
      
      





 Observable<String> queryTitle(String url) { WebParsing webParsing = new WebParsing(); return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext(webParsing.getTitle(url)); subscriber.onCompleted(); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); }
      
      





最初のObservableはサイトで見つかったリンクのURLのリストを生成し、2番目はTitleを生成します。 フェザーメソッドの例を詳細に、行ごとに分析してみましょう。

  1. Observable <List> queryURLs(String url) -文字列はObservableメソッドを通知します。このメソッドは、入力パラメーターとして解析するサイトへのリンクを取得し、指定されたサイトからの<List>リンクのリストとして解析結果を返します。

    WebParsing webParsing = new WebParsing() -解析関数にアクセスするための変数を作成します。

    return Observable.create-リンクのリストを返すObservableを作成します。

    new Observable.OnSubscribe <List>() -行は、サブスクリプション時に呼び出される1つのメソッド(以下を参照)でOnSubscribeインターフェイスを宣言します。

    public void call(サブスクライバー<?super List>サブスクライバー) -サブスクライバーサブスクリプション後に呼び出されるcallメソッドをオーバーロードします。

    subscriber.onNext(webParsing.getURLs(url)) -onNextメソッドを呼び出して、データが生成されるたびにサブスクライバーデータを送信します。 このメソッドは、Observableによって発行されたオブジェクトをパラメーターとして受け取ります。

    subscriber.onCompleted() -Observableは、エラーが検出されなかった場合に最後にonNextを呼び出した後、onCompleted()メソッドを呼び出します。

    subscribeOn(Schedulers.io()) -subscribeOnメソッドは、すべてのObservableアップストリームをSchedulers.io()スケジューラーにサブスクライブします。

    observeOn(AndroidSchedulers.mainThread()) -observeOnメソッドを使用すると、メインアプリケーションスレッドで結果を取得できます。


5.最初のリアクティブアプリケーションを起動します



したがって、Observablesが作成されます。上記の最初の方法に基づいて、サイトリンクのリストを表示する最も単純な例を実装します。

 public void example0(final TextView textView, String url) { queryURLs(url) .subscribe(new Action1<List<String>>() { @Override public void call(List<String> urls) { for (String url: urls) { String string = (String) textView.getText(); textView.setText(string + url + "\n\n"); } } }); }
      
      





MainExampleクラスで実装された例をラップし、MainActivityで呼び出します。

 public class MainActivity extends AppCompatActivity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); MainExample mainExample = new MainExample(); mainExample.example0(textView, "https://yandex.ru/"); } }
      
      





6.反応性の向上-演算子の使用



Observableは演算子を使用して出力を変換でき、ObservableとSubscriberの間で使用してデータを操作できます。 RxJavaには多くの演算子がありますので、まずは最も人気のあるものを見てみましょう。

そもそも、サブスクライバーのループを取り除き、オブザーバーに、受信したリンクの配列からデータを順番に送信するように強制します。from()演算子はこれを支援します。

 public void example1(final TextView textView, String url) { queryURLs(url) .subscribe(new Action1<List<String>>() { @Override public void call(List<String> urls) { Observable.from(urls) .subscribe(new Action1<String>() { @Override public void call(String url) { String string = (String) textView.getText(); textView.setText(string + url + "\n\n"); } }); } }); }
      
      





見た目は美しくなく、少しわかりにくいので、次のflatMap()演算子を使用します。この演算子は、あるObservableから送信されたデータを受け取り、別のObservableから送信されたデータを返します。

 public void example2(final TextView textView, String url) { queryURLs(url) .flatMap(new Func1<List<String>, Observable<String>>() { @Override public Observable<String> call(List<String> urls) { return Observable.from(urls); } }) .subscribe(new Action1<String>() { @Override public void call(String url) { String string = (String) textView.getText(); textView.setText(string + url + "\n\n"); } }); }
      
      





次のステップでは、サブスクライバーをアンロードし、map()演算子を使用します。これにより、1つのデータ項目を別のデータ項目に変換できます。 map()演算子は、データを変換し、元のデータとは異なる必要なタイプのデータを生成することもできます。 この場合、オブザーバーは行のリストを作成し、サブスクライバーはそれらを画面にのみ表示します。

 public void example3(final TextView textView, String url) { queryURLs(url) .flatMap(new Func1<List<String>, Observable<String>>() { @Override public Observable<String> call(List<String> urls) { return Observable.from(urls); } }) .map(new Func1<String, String>() { @Override public String call(String url) { return textView.getText() + url + "\n\n"; } }) .subscribe(new Action1<String>() { @Override public void call(String url) { textView.setText(url); } }); }
      
      





主な機能を調べたので、今度はラムダを使用してコードを簡素化します。

 queryURLs(url) .flatMap(urls -> Observable.from(urls)) .map(url1 -> textView.getText() + url1 + "\n\n") .subscribe(url1 -> { textView.setText(url1); });
      
      





またはさらに簡単:

 queryURLs(url) .flatMap(Observable::from) .map(url1 -> textView.getText() + url1 + "\n\n") .subscribe(textView::setText);
      
      





上記の構成と結果のコードを比較して、ラムダ式の威力とシンプルさを感じてください。



7.力を増す



次のステップでは、処理を複雑にし、flatMap()演算子を使用して、準備された2番目のqueryTitle()メソッドを接続します。このメソッドもオブザーバーを返します。 このメソッドは、サイトへのリンクによってサイトのタイトルを返します。 Webページにあるリンクを使用してWebサイトヘッダーのリストを作成および表示する例を作成しましょう。 前の例で受け取ったサイトへのリンクのリストの代わりに、これらのサイトのタイトルを表示します。

 public void example4(final TextView textView, String url) { queryURLs(url) .flatMap(new Func1<List<String>, Observable<String>>() { @Override public Observable<String> call(List<String> urls) { return Observable.from(urls); } }) .flatMap(new Func1<String, Observable<String>>() { @Override public Observable<String> call(String url) { return queryTitle(url); } }) .subscribe(new Action1<String>() { @Override public void call(String title) { textView.setText(title); } }); }
      
      





または省略形:

 queryURLs(url) .flatMap(Observable::from) .flatMap(this::queryTitle) .subscribe(textView::setText);
      
      





map()を追加して、ヘッダーのリストを作成します。

 queryURLs(url) .flatMap(Observable::from) .flatMap(this::queryTitle) .map(url1 -> textView.getText() + url1 + "\n\n") .subscribe(textView::setText);
      
      





filter()演算子を使用して、空の行をnull値で除外します。

 queryURLs(url) .flatMap(Observable::from) .flatMap(this::queryTitle) .filter(title -> title != null) .map(url1 -> textView.getText() + url1 + "\n\n") .subscribe(textView::setText);
      
      





take()演算子を使用して、最初の7つのヘッダーのみを取得します。

 queryURLs(url) .flatMap(Observable::from) .flatMap(this::queryTitle) .filter(title -> title != null) .take(7) .map(url1 -> textView.getText() + url1 + "\n\n") .subscribe(textView::setText);
      
      





最後の例は、多くのメソッドの組み合わせ、利用可能な多数の演算子の使用、ラムダ式を示し、文字通り、さまざまなデータのストリームの強力なハンドラーになることを示しました。



記事に記載されているすべての例はここに掲載されています



ソース:



  1. 公式文書
  2. Grokai * RxJava、パート1:基本
  3. AndroidでReactiveXを使い始める
  4. RxJava-チュートリアル
  5. RxJavaとAndroidの使用開始
  6. AndroidでのRxJavaを使用したリアクティブプログラミング
  7. RxJava、RxAndroid、Retrolambdaによるパーティートリック



All Articles