apeescape2.com
  • メイン
  • 財務プロセス
  • データサイエンスとデータベース
  • 人とチーム
  • 製品ライフサイクル
モバイル

RxJavaに会う:Android用のミッシングリアクティブプログラミングライブラリ

あなたがAndroid開発者なら、聞いたことがあるかもしれません RxJava 。これは、Android開発でリアクティブプログラミングを有効にするための最も議論されているライブラリの1つです。これは、モバイルプログラミングに固有の同時実行/非同期タスクを簡素化するための頼りになるフレームワークとして宣伝されています。

しかし…RxJavaとは何ですか、そしてそれはどのように物事を「単純化」するのでしょうか?

Android用関数型リアクティブプログラミング:RxJavaの概要



RxJavaを使用して、Androidをあまりにも多くのJavaスレッドから解き放ちます。 つぶやき

RxJavaとは何かを説明するオンラインですでに利用可能なリソースはたくさんありますが、この記事での私の目標は、RxJavaの基本的な紹介、特にAndroid開発への適合性を説明することです。また、新しいプロジェクトまたは既存のプロジェクトに統合する方法について、具体的な例と提案をいくつか示します。

なぜRxJavaを検討するのですか?

コアとなるRxJavaは、開発を簡素化します。 抽象化のレベルを上げます スレッドの周り。つまり、開発者は、さまざまなスレッドで発生する必要のある操作を実行する方法の詳細についてあまり心配する必要はありません。スレッド化は正しく実行するのが難しく、正しく実装されていない場合、デバッグと修正が最も難しいバグのいくつかを引き起こす可能性があるため、これは特に魅力的です。

確かに、これはスレッド化に関してRxJavaが防弾であることを意味するものではなく、舞台裏で何が起こっているのかを理解することは依然として重要です。ただし、RxJavaは間違いなくあなたの生活を楽にすることができます。

例を見てみましょう。

ネットワーク呼び出し-RxJavaとAsyncTask

ネットワーク経由でデータを取得し、その結果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

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の文書を読むことはかなり簡単で要領を得ています。ドキュメントの各オペレーターには、オペレーターがストリームにどのように影響するかについての視覚化が付属しています。これらの視覚化は「マーブルダイアグラム」と呼ばれます。

フリップと呼ばれる架空の演算子を大理石の図でモデル化する方法は次のとおりです。

フリップと呼ばれる架空の演算子を大理石の図でモデル化する方法の例。

RxJavaを使用したマルチスレッド

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 図書館。

Android上のRxJava

いくつかの基本事項がわかったので、疑問に思われるかもしれません。RxJavaを統合するための最良の方法は何ですか。 アンドロイド 応用?ご想像のとおり、RxJavaには多くのユースケースがありますが、この例では、ネットワークスタックの一部としてObservableオブジェクトを使用するという1つの特定のケースを見てみましょう。

この例では、 後付け 、GitHubのAPIと対話するためのRxJavaとの組み込みバインディングを備えたSquareによってオープンソース化されたHTTPクライアント。具体的には、GitHubユーザー名を指定されたユーザーのスター付きリポジトリをすべて表示するシンプルなアプリを作成します。先に進みたい場合は、ソースコードを利用できます ここに 。

新しいAndroidプロジェクトを作成する

  • 新しいAndroidプロジェクトを作成し、名前を付けることから始めます GitHubRxJava 。

スクリーンショット:新しいAndroidプロジェクトを作成する

  • の中に ターゲットAndroidデバイス 画面、維持 携帯電話とタブレット 選択して、SDKの最小レベルを17に設定します。APIレベルを低く/高く設定してください。ただし、この例では、APIレベル17で十分です。

スクリーンショット:ターゲットAndroidデバイス画面

  • 選択する 空のアクティビティ 次のプロンプトで。

スクリーンショット:モバイル画面にアクティビティを追加する

MacでAndroidアプリを開発する
  • 最後のステップで、アクティビティ名を次のように保持します MainActivity レイアウトファイルを生成します activity_main 。

スクリーンショット:アクティビティ画面をカスタマイズする

プロジェクトの設定

含める 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機能

技術的負債の経済的影響

財務プロセス

技術的負債の経済的影響
クリーンなアーキテクチャでMVVMを使用したより優れたAndroidアプリ

クリーンなアーキテクチャでMVVMを使用したより優れたAndroidアプリ

モバイル

人気の投稿
マーケットプレイスオペレーションディレクター
マーケットプレイスオペレーションディレクター
SMACSSの探索:CSSのスケーラブルでモジュラーなアーキテクチャ
SMACSSの探索:CSSのスケーラブルでモジュラーなアーキテクチャ
Rubyメタプログラミングは思ったよりもクールです
Rubyメタプログラミングは思ったよりもクールです
スケッチとルーパーを使ってすぐに心を曲げるイラストを作成する
スケッチとルーパーを使ってすぐに心を曲げるイラストを作成する
GraphQLとREST-GraphQLチュートリアル
GraphQLとREST-GraphQLチュートリアル
 
生体認証セキュリティ–パスワードなしの認証または流行の鍵?
生体認証セキュリティ–パスワードなしの認証または流行の鍵?
疲労感のために996をダイヤルする:燃え尽き症候群の回避を優先する方法
疲労感のために996をダイヤルする:燃え尽き症候群の回避を優先する方法
ランディングページのデザイン:究極のランディングページの構築
ランディングページのデザイン:究極のランディングページの構築
H-1Bビザ:ホンジュラスからシリコンバレーへのiOS開発者の旅
H-1Bビザ:ホンジュラスからシリコンバレーへのiOS開発者の旅
ローグワンチームシニアバックエンドエンジニア
ローグワンチームシニアバックエンドエンジニア
人気の投稿
  • nodejs静的サイトジェネレーター
  • ウォーレンバフェットはどのように投資しますか
  • javascriptで日付を取得する方法
  • 外国為替リスクをヘッジする方法
  • 設計定義の要素と原則
  • wrike vs asana vs basecamp
  • C ++のコーディング方法を学ぶ
カテゴリー
製品の担当者とチーム アジャイルタレント 収益性と効率性 計画と予測 バックエンド 製品ライフサイクル ブランドデザイン エンジニアリング管理 仕事の未来 プロセスとツール

© 2021 | 全著作権所有

apeescape2.com