あなたがAndroid開発者なら、聞いたことがあるかもしれません RxJava 。これは、Android開発でリアクティブプログラミングを有効にするための最も議論されているライブラリの1つです。これは、モバイルプログラミングに固有の同時実行/非同期タスクを簡素化するための頼りになるフレームワークとして宣伝されています。
しかし…RxJavaとは何ですか、そしてそれはどのように物事を「単純化」するのでしょうか?
RxJavaとは何かを説明するオンラインですでに利用可能なリソースはたくさんありますが、この記事での私の目標は、RxJavaの基本的な紹介、特にAndroid開発への適合性を説明することです。また、新しいプロジェクトまたは既存のプロジェクトに統合する方法について、具体的な例と提案をいくつか示します。
コアとなるRxJavaは、開発を簡素化します。 抽象化のレベルを上げます スレッドの周り。つまり、開発者は、さまざまなスレッドで発生する必要のある操作を実行する方法の詳細についてあまり心配する必要はありません。スレッド化は正しく実行するのが難しく、正しく実装されていない場合、デバッグと修正が最も難しいバグのいくつかを引き起こす可能性があるため、これは特に魅力的です。
確かに、これはスレッド化に関してRxJavaが防弾であることを意味するものではなく、舞台裏で何が起こっているのかを理解することは依然として重要です。ただし、RxJavaは間違いなくあなたの生活を楽にすることができます。
例を見てみましょう。
ネットワーク経由でデータを取得し、その結果UIを更新するとします。これを行う1つの方法は、(1)内部を作成することですAsyncTask
Activity
/ Fragment
のサブクラスは、(2)バックグラウンドでネットワーク操作を実行し、(3)その操作の結果を取得して、メインスレッドのUIを更新します。
public class NetworkRequestTask extends AsyncTask { private final int userId; public NetworkRequestTask(int userId) { this.userId = userId; } @Override protected User doInBackground(Void... params) { return networkService.getUser(userId); } @Override protected void onPostExecute(User user) { nameTextView.setText(user.getName()); // ...set other views } } private void onButtonClicked(Button button) { new NetworkRequestTask(123).execute() }
これは無害に見えるかもしれませんが、このアプローチにはいくつかの問題と制限があります。つまり、NetworkRequestTask
以降、メモリ/コンテキストリークが簡単に発生します。は内部クラスであるため、外部クラスへの暗黙の参照を保持します。また、ネットワーク呼び出しの後に別の長い操作をチェーンしたい場合はどうなりますか? 2つのAsyncTask
をネストする必要があるため、読みやすさが大幅に低下する可能性があります。
対照的に、ネットワーク呼び出しを実行するためのRxJavaアプローチは、次のようになります。
private Subscription subscription; private void onButtonClicked(Button button) { subscription = networkService.getObservableUser(123) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1() { @Override public void call(User user) { nameTextView.setText(user.getName()); // ... set other views } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); }
このアプローチを使用して、返されたSubscription
への参照を保持することにより、(外部コンテキストへの参照を保持している実行中のスレッドによって引き起こされる潜在的なメモリリークの)問題を解決します。オブジェクト。これSubscription
次に、オブジェクトはActivity
/ Fragment
に関連付けられます。オブジェクトの#onDestroy()
Action1#call
を保証する方法Activity
/ Fragment
の場合、操作は実行されません。破壊する必要があります。
PythonでTwitterデータをマイニング
また、戻り値の型が#getObservableUser(...)
であることに注意してください。 (つまり、Observable
)は、それへのさらなる呼び出しと連鎖しています。この流動的なAPIを通じて、AsyncTask
を使用するという2番目の問題を解決することができます。これは、さらなるネットワーク呼び出し/長い操作の連鎖を可能にするということです。かなりきちんとしていますね
RxJavaの概念について詳しく見ていきましょう。
RxJavaの世界では、すべてをストリームとしてモデル化できます。ストリームは時間の経過とともにアイテムを放出し、各放出は消費/観察できます。
考えてみれば、ストリームは新しい概念ではありません。クリックイベントはストリーム、位置情報の更新はストリーム、プッシュ通知はストリームなどです。
ストリームの抽象化は、私が「3つのO」と呼んでいる3つのコア構造を通じて実装されます。すなわち: または bservable、 または bserver、および または 拮抗薬。インクルード 観察可能 アイテム(ストリーム)を放出します。そしてその 観察者 それらのアイテムを消費します。 Observableオブジェクトからの放出は、連鎖によってさらに変更、変換、および操作できます。 オペレーター 呼び出します。
Observableは、RxJavaのストリーム抽象化です。それはに似ています イテレータ その中で、シーケンスが与えられると、それはそれらのアイテムを順番に繰り返して生成します。コンシューマーは、基になるシーケンスに関係なく、同じインターフェイスを介してこれらのアイテムを消費できます。
番号1、2、3をこの順序で発行したいとします。そのために、Observable#create(OnSubscribe)
を使用できます方法。
Observable observable = Observable.create(new Observable.OnSubscribe() { @Override public void call(Subscriber subscriber) { subscriber.onNext(1); subscriber.onNext(2); subscriber.onNext(3); subscriber.onCompleted(); } });
呼び出しsubscriber.onNext(Integer)
ストリーム内のアイテムを放出し、ストリームの放出が終了すると、subscriber.onCompleted()
次に呼び出されます。
Observableを作成するためのこのアプローチは、かなり冗長です。このため、ほとんどすべての場合に優先される必要があるObservableインスタンスを作成するための便利な方法があります。
Observableを作成する最も簡単な方法は、Observable#just(...)
を使用することです。メソッド名が示すように、メソッド引数として渡したアイテムを出力するだけです。
javascriptでAPIを作成する方法
Observable.just(1, 2, 3); // 1, 2, 3 will be emitted, respectively
Observableストリームの次のコンポーネントは、サブスクライブされているObserver(またはオブザーバー)です。ストリームで「興味深い」何かが発生すると、オブザーバーに通知されます。オブザーバーは、次のイベントを介して通知されます。
Observer#onNext(T)
-アイテムがストリームから発行されたときに呼び出されますObservable#onError(Throwable)
-ストリーム内でエラーが発生したときに呼び出されますObservable#onCompleted()
-ストリームがアイテムの送信を終了したときに呼び出されます。ストリームをサブスクライブするには、Observable#subscribe(...)
を呼び出すだけです。オブザーバーインスタンスを渡します。
asana vs wrike vs trello
Observable observable = Observable.just(1, 2, 3); observable.subscribe(new Observer() { @Override public void onCompleted() { Log.d('Test', 'In onCompleted()'); } @Override public void onError(Throwable e) { Log.d('Test', 'In onError()'); } @Override public void onNext(Integer integer) { Log.d('Test', 'In onNext():' + integer); } });
上記のコードは、Logcatで次のように出力します。
In onNext(): 1 In onNext(): 2 In onNext(): 3 In onNext(): 4 In onCompleted()
また、Observableの排出量に関心がなくなった場合もあります。これは、たとえばActivity
/ Fragment
の場合にAndroidで特に関係があります。メモリ内で再利用する必要があります。
アイテムの監視を停止するには、Subscription#unsubscribe()
を呼び出すだけです。返されたSubscriptionオブジェクト。
Subscription subscription = someInfiniteObservable.subscribe(new Observer() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } }); // Call unsubscribe when appropriate subscription.unsubscribe();
上記のコードスニペットに見られるように、Observableにサブスクライブすると、返されたサブスクリプションオブジェクトへの参照を保持し、後でsubscription#unsubscribe()
を呼び出します。必要に応じて。 Androidでは、これはActivity#onDestroy()
内で呼び出すのが最適です。またはFragment#onDestroy()
。
Observableによって発行されたアイテムは、サブスクライブされたObserverオブジェクトに通知する前に、Operatorを介して変換、変更、およびフィルタリングできます。関数型プログラミングで見られる最も一般的な操作のいくつか(map、filter、reduceなど)は、Observableストリームにも適用できます。例として地図を見てみましょう。
Observable.just(1, 2, 3, 4, 5).map(new Func1() { @Override public Integer call(Integer integer) { return integer * 3; } }).subscribe(new Observer() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });
上記のコードスニペットは、Observableからの各放出を取得し、それぞれに3を掛けて、それぞれストリーム3、6、9、12、15を生成します。演算子を適用すると、通常、結果として別のObservableが返されます。これにより、複数の操作を連鎖させて目的の結果を得ることができるので便利です。
上記のストリームを考えると、偶数のみを受信したいとします。これは、チェーンすることによって達成できます フィルタ 操作。
Observable.just(1, 2, 3, 4, 5).map(new Func1() { @Override public Integer call(Integer integer) { return integer * 3; } }).filter(new Func1() { @Override public Boolean call(Integer integer) { return integer % 2 == 0; } }).subscribe(new Observer() { @Override public void onCompleted() { // ... } @Override public void onError(Throwable e) { // ... } @Override public void onNext(Integer integer) { // ... } });
がある RxJavaに組み込まれている多くの演算子 Observableストリームを変更するツールセット。ストリームを変更する方法を考えることができれば、おそらくそのためのオペレーターがいます。ほとんどの技術文書とは異なり、RxJava / ReactiveXの文書を読むことはかなり簡単で要領を得ています。ドキュメントの各オペレーターには、オペレーターがストリームにどのように影響するかについての視覚化が付属しています。これらの視覚化は「マーブルダイアグラム」と呼ばれます。
フリップと呼ばれる架空の演算子を大理石の図でモデル化する方法は次のとおりです。
Observableチェーンで操作が発生するスレッドの制御は、 スケジューラー その中でオペレーターが発生する必要があります。基本的に、スケジューラーは、指定されたときにオペレーターが使用して実行するスレッドプールと考えることができます。デフォルトでは、そのようなスケジューラーが提供されていない場合、ObservableチェーンはObservable#subscribe(...)
と同じスレッドで動作します。と呼ばれます。それ以外の場合、スケジューラーはObservable#subscribeOn(Scheduler)
を介して指定できます。および/またはObservable#observeOn(Scheduler)
ここで、スケジュールされた操作は、スケジューラーによって選択されたスレッドで発生します。
2つの方法の主な違いは、Observable#subscribeOn(Scheduler)
です。どのスケジューラーで実行するかをソースObservableに指示します。チェーンは、Observable#subscribeOn(Scheduler)
で指定されたスケジューラーからのスレッドで引き続き実行されます。 Observable#observeOn(Scheduler)
への呼び出しまで別のスケジューラで作成されています。このような呼び出しが行われると、それ以降のすべてのオブザーバー(つまり、チェーンの後続の操作)は、observeOn
から取得したスレッドで通知を受け取ります。スケジューラ。
これらのメソッドが操作の実行場所にどのように影響するかを示す大理石の図を次に示します。
Androidのコンテキストでは、長い操作の結果としてUI操作を実行する必要がある場合は、その操作をUIスレッドで実行する必要があります。この目的のために、で提供されているスケジューラーの1つであるAndroidScheduler#mainThread()
を使用できます。 RxAndroid 図書館。
いくつかの基本事項がわかったので、疑問に思われるかもしれません。RxJavaを統合するための最良の方法は何ですか。 アンドロイド 応用?ご想像のとおり、RxJavaには多くのユースケースがありますが、この例では、ネットワークスタックの一部としてObservableオブジェクトを使用するという1つの特定のケースを見てみましょう。
この例では、 後付け 、GitHubのAPIと対話するためのRxJavaとの組み込みバインディングを備えたSquareによってオープンソース化されたHTTPクライアント。具体的には、GitHubユーザー名を指定されたユーザーのスター付きリポジトリをすべて表示するシンプルなアプリを作成します。先に進みたい場合は、ソースコードを利用できます ここに 。
MacでAndroidアプリを開発する
含める RxJava 、 RxAndroid 、 そしてその 後付け app/build.gradle
のライブラリ。 RxAndroidを含めると、暗黙的にRxJavaも含まれることに注意してください。ただし、RxAndroidには常に最新バージョンのRxJavaが含まれているとは限らないため、これら2つのライブラリを常に明示的に含めることをお勧めします。 RxJavaの最新バージョンを明示的に含めると、最新バージョンの使用が保証されます。
dependencies { compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'io.reactivex:rxandroid:1.2.0' compile 'io.reactivex:rxjava:1.1.8' // ...other dependencies }
GitHubRepo
を作成しますデータオブジェクトクラス。このクラスはGitHubのリポジトリをカプセル化します(ネットワーク応答にはより多くのデータが含まれていますが、関心があるのはそのサブセットのみです)。
public class GitHubRepo { public final int id; public final String name; public final String htmlUrl; public final String description; public final String language; public final int stargazersCount; public GitHubRepo(int id, String name, String htmlUrl, String description, String language, int stargazersCount) { this.id = id; this.name = name; this.htmlUrl = htmlUrl; this.description = description; this.language = language; this.stargazersCount = stargazersCount; } }
GitHubService
を作成しますインターフェース。このインターフェースをRetrofitに渡し、RetrofitはGitHubService
の実装を作成します。 public interface GitHubService { @GET('users/{user}/starred') Observable getStarredRepositories(@Path('user') String userName); }
GitHubClient
を作成しますクラス。これは、UIレベルからネットワーク呼び出しを行うために対話するオブジェクトになります。
GitHubService
の実装を構築する場合Retrofitを介して、RxJavaCallAdapterFactory
を渡す必要がありますネットワーク呼び出しがObservableオブジェクトを返すことができるように呼び出しアダプタとして(Call
以外の結果を返すネットワーク呼び出しには呼び出しアダプタを渡す必要があります)。
また、GsonConverterFactory
を渡す必要があります使用できるように グソン JSONオブジェクトをJavaオブジェクトにマーシャリングする方法として。
public class GitHubClient { private static final String GITHUB_BASE_URL = 'https://api.github.com/'; private static GitHubClient instance; private GitHubService gitHubService; private GitHubClient() { final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); final Retrofit retrofit = new Retrofit.Builder().baseUrl(GITHUB_BASE_URL) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build(); gitHubService = retrofit.create(GitHubService.class); } public static GitHubClient getInstance() { if (instance == null) { instance = new GitHubClient(); } return instance; } public Observable getStarredRepos(@NonNull String userName) { return gitHubService.getStarredRepositories(userName); } }
次に、入力されたGitHubユーザー名を指定して、取得したリポジトリを表示する単純なUIを作成します。作成activity_home.xml
-私たちの活動のレイアウト-次のようなもので:
item_github_repo.xml
作成ListView
-ListAdapter
GitHubリポジトリオブジェクトのアイテムレイアウト-次のようなもの:
GitHubRepo
ListView
を作成します製本を担当しているitem_github_repo.xml
オブジェクトをView
にアイテム。このプロセスには、基本的にView
の膨張が含まれます。 View
にリサイクルされていない場合View
供給される;それ以外の場合は、リサイクルされたpublic class GitHubRepoAdapter extends BaseAdapter { private List gitHubRepos = new ArrayList(); @Override public int getCount() { return gitHubRepos.size(); } @Override public GitHubRepo getItem(int position) { if (position = gitHubRepos.size()) { return null; } else { return gitHubRepos.get(position); } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = (convertView != null ? convertView : createView(parent)); final GitHubRepoViewHolder viewHolder = (GitHubRepoViewHolder) view.getTag(); viewHolder.setGitHubRepo(getItem(position)); return view; } public void setGitHubRepos(@Nullable List repos) { if (repos == null) { return; } gitHubRepos.clear(); gitHubRepos.addAll(repos); notifyDataSetChanged(); } private View createView(ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View view = inflater.inflate(R.layout.item_github_repo, parent, false); final GitHubRepoViewHolder viewHolder = new GitHubRepoViewHolder(view); view.setTag(viewHolder); return view; } private static class GitHubRepoViewHolder { private TextView textRepoName; private TextView textRepoDescription; private TextView textLanguage; private TextView textStars; public GitHubRepoViewHolder(View view) { textRepoName = (TextView) view.findViewById(R.id.text_repo_name); textRepoDescription = (TextView) view.findViewById(R.id.text_repo_description); textLanguage = (TextView) view.findViewById(R.id.text_language); textStars = (TextView) view.findViewById(R.id.text_stars); } public void setGitHubRepo(GitHubRepo gitHubRepo) { textRepoName.setText(gitHubRepo.name); textRepoDescription.setText(gitHubRepo.description); textLanguage.setText('Language: ' + gitHubRepo.language); textStars.setText('Stars: ' + gitHubRepo.stargazersCount); } } }
過度の膨張を防ぐために再利用されますMainActivity
オブジェクト。
Activity
public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private GitHubRepoAdapter adapter = new GitHubRepoAdapter(); private Subscription subscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ListView listView = (ListView) findViewById(R.id.list_view_repos); listView.setAdapter(adapter); final EditText editTextUsername = (EditText) findViewById(R.id.edit_text_username); final Button buttonSearch = (Button) findViewById(R.id.button_search); buttonSearch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String username = editTextUsername.getText().toString(); if (!TextUtils.isEmpty(username)) { getStarredRepos(username); } } }); } @Override protected void onDestroy() { if (subscription != null && !subscription.isUnsubscribed()) { subscription.unsubscribe(); } super.onDestroy(); } private void getStarredRepos(String username) { subscription = GitHubClient.getInstance() .getStarredRepos(username) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer
ですべてを接着します。これは本質的に() { @Override public void onCompleted() { Log.d(TAG, 'In onCompleted()'); } @Override public void onError(Throwable e) { e.printStackTrace(); Log.d(TAG, 'In onError()'); } @Override public void onNext(List gitHubRepos) { Log.d(TAG, 'In onNext()'); adapter.setGitHubRepos(gitHubRepos); } }); } }
|_+_|ですこれは、アプリを最初に起動したときに表示されます。ここでは、ユーザーにGitHubユーザー名を入力してもらい、最後に、スター付きのすべてのリポジトリをそのユーザー名で表示します。
私はどのタイプのLLCを持っていますか
|_+_|
アプリを実行すると、GitHubユーザー名を入力するための入力ボックスが画面に表示されます。検索すると、スター付きのすべてのリポジトリのリストが表示されます。
これがRxJavaの有用な紹介とその基本機能の概要として役立つことを願っています。 RxJavaには強力な概念がたくさんあります。十分に文書化されているものをさらに深く掘り下げて、それらを探索することをお勧めします。 RxJava wiki 。
下のコメントボックスに質問やコメントを残してください。 Twitterで私をフォローすることもできます。 @arriolachris ここで私はRxJavaとAndroidのすべてについてたくさんツイートしています。
RxJavaの包括的な学習リソースが必要な場合は、 電子ブック LeanpubでAngusHuangと一緒に書いた。
関連: Android開発を後押しする10のKotlin機能