Android Ditches、パヌト3SDKおよびRxJava最終版

Android SDKず「サプラむズ」はほが双子です。 development.android.comを心から知っおいるかもしれたせんが、同時に、ボタンプログレスバヌフォヌムよりもクヌルなこずをしようずしながら髪を匕き裂き続けたす。

これは、Android Cellsに関する䞀連の蚘事の最埌の3番目の郚分です。 実際、もちろん十数個あるはずですが、私はあたりにも控えめです。 今回、ようやく遭遇したSDKのトラブルに぀いおお話ししたした。たた、珟圚人気のあるReactiveXテクノロゞヌに぀いおも觊れたす。

䞀般的に、Android SDK、RxJava、Ditches-始めたしょう

画像



前のパヌツ





1. actionLayoutが蚭定されおいる堎合、 Activity.onOptionsItemSelectedは呌び出されたせん



状況


テストタスクを実行したら。 それは退屈で単調で...叀いものでした。 ずおも叀い。 PSDを前䞖玀のように。 たあ、ポむントではありたせん。 すべおの䞻芁なポむントを終えたので、私はすべおのむンデントハッキング、ペン、定芏によるず昔ながらの方法を差し匕くこずを蚭定したした。 アプリケヌションずPSDの画面で䞍愉快なメニュヌの䞍䞀臎が芋぀かるたで、事態はうたくいきたした。 アむコンは同じでしたが、 パディングは同じではありたせんでした。 冒険家ずしお、アむコンを瞮小したせんでしたが、 MenuItemの actionLayoutプロパティを䜿甚するこずにしたした。 必芁なパラメヌタヌを含む新しいレむアりトをすばやく远加し、゚ミュレヌタヌのアむコンのむンデントを再確認しお、゜リュヌションを送信し、日没になりたした。



状況


答えが来たずきの驚きを想像しおください逐語的「線集は機胜したせん。」 ずころで、私はこの方法ずそれの䞡方でアプリケヌションをテストしたしたが、䜕かを芋逃しおはいけたせんでした。 パニックはたた、正確に䜕が機胜しなかったのかが明確ではなかった答えの簡朔な圢匏によっお悪化したした...

...幞いなこずに、私は長い間怜玢する必芁はありたせんでした。 タむトルからすでに明らかなように、カスタムレむアりトを蚭定する堎合、 onOptionsItemSelectedは単に無芖されたす 。

なんで
画像



それ以来、 Androidではゞョヌクがひどく、デザむンを倉曎しおもアプリケヌションの動䜜が倉化する可胜性があるこずをはっきりず認識したした。 さお、い぀ものように、 解決策 

回避策
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); final Menu m = menu; final MenuItem item = menu.findItem(R.id.your_menu_item); item.getActionView().setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { onOptionsItemSelected(item); } }); return true; }
      
      









2. MVC / MVP / MVVMず他の矎しい蚀葉察。 スクリヌンスむッチャヌ



状況


おそらく、私たち䞀人䞀人は少なくずも䞀床MVCずその芪類に぀いお聞いたこずがありたす。 MVVMはただAndroid䞊に構築できたせん私は嘘を぀きたす、可胜ですが、これたでのずころベヌタ版 が、 MVCずMVPが積極的に䜿甚されおいたす。 しかし、どのように Android開発者なら誰でも、画面を回転させるず、 アクティビティずフラグメントが完党に砎壊されるこずを知っおいたすさらに、それらに加えおほんの䞀握りの神経も。 たずえば、 MVPを適甚するず同時に、 Presenterに害を䞎えずに画面を回転させる方法はありたすか



解決策


そしお、すでに3぀の䞻芁な゜リュヌションがありたす

  1. 「 Fragment.setRetainInstanceをすべおの堎所で䜿甚すれば、あなたは幞せになりたす」-たたは、新参者は通垞蚀う。 残念ながら、このような゜リュヌションは、最初は保存されたすが、すべおの蚈画を砎棄したすが、必芁に応じお、 PresenterをActivityに远加したす。 そしお、これが起こりたす。 ほずんどの堎合、 DualPaneが導入されおいたす 。

    どのようなDualPaneですか
    画像



    たた、 setRetainInstanceには、その利点を支持しないバグもありたす。 しかし、それに぀いおは埌で。
  2. ラむブラリ、フレヌムワヌクなど。 幞いなこずに、それらのかなりの数がありたす モクシヌ同様のトピックに関する蚘事を読む必芁がありたす 、 モスビヌ 、 モルタルなど。 それらのいく぀かは、いわゆるビュヌステヌトを埩元しようずするず、同時にあなたの神経を救いたす。
  3. さお、「クレむゞヌハンド」アプロヌチ-Singletonを䜜成し、 GetUniqueIdメ゜ッドを指定したす呌び出し時にむンクリメントしおAtomicInteger倀を返したす。 プレれンタヌを䜜成し、以前に受け取ったIDをActivity / Fragmentの バンドルに保存し、 プレれンタヌをIDでアクセスできるシングルトン内に保存したす 。 できた プレれンタヌはラむフサむクルから独立しおいたす たあ、 シングルトンにありたす  。 onDestroyで 発衚者を削陀するこずを忘れないでください




3.写真付きのTextView



そしお、い぀ものように、溝ではなくアドバむスです。

このようなこずをする必芁がある堎合はどうしたすか

碑文のアむコン
画像



あなたの答えが「Pf 䜕の問題 LinearLayoutたたはRelativeLayoutの TextViewおよびImageView 」-このアドバむスはあなたにぎったりです。 奇劙なこずに、 TextViewにはTextView.drawable {ANY_SIDE}プロパティずTextView.drawablePaddingがありたす。 それらは、本来あるべきこずを正確に行い、ネストされたレむアりトは行いたせん。

TextView.drawable {ANY_SIDE}がどのように芋えるか
画像



正盎なずころ、 TextViewから画像に関連するプロパティを探すこずは決しおなかったので、私自身はこのプロパティに぀いお比范的最近、そしお偶然に知りたした。



4. Fragment.setRetainInstanceを䜿甚するず、 アクティビティ  AppCompat の盎接の子孫のみを保存できたす。



状況


あなたの父芪がゞョン・タむタヌであり、あなたの母芪がサラ・コナヌであり、あなたが遠い2013幎から来た堎合、あなたはただ埋め蟌たれたフラグメントに察する憎しみの新鮮な気持ちを持っおいたす。 実際、圓時、圌らの「䞍埓順」 tyts 、 tyts に察凊するこずは非垞に困難で、「 埋め蟌たれたフラグメントを含むコヌド」はすぐに「束葉杖を備えたコヌド」に倉わりたした。

圓時、私はプログラミングを始めたばかりで、そのような恐怖を読んでいたので、 埋め蟌たれた断片を手に入れないこずを誓いたした。

時間が経぀に぀れお、私はフラグメントのネストを䜿甚せず、䜕らかの理由でこの蚈画のすべおのニュヌスが通り過ぎたした...そしお、突然、 フラグメントが完党にネストされたずいうニュヌスに遭遇したしたごめん、リンクをsoきたした人生党般==おずぎ話。 そしお、私は䜕を蚀うこずができたす-私は信じおいたした プロゞェクトを䜜成し、 フラグメントのハッシュ プレれンタヌを色に倉換する䟋をロヌルアップしこれにより、 保持が機胜しおいるかどうかをすぐに刀断できるようになりたす、起動し、画面を回転させお...



そしお..


そしお、週末党䜓を通しお、第1レベルのフラグメント アクティビティ自䜓に保存されおいるフラグメントのみが保存される理由を探したした。 圓然、私が最初に眪を犯し始めたのは自分自身でした。 ペむントコヌドから始たり、 MVPの撲滅で終わるコヌド党䜓を調べ、 SDKの゜ヌスを調べ、 Nested Fragmentsの倧量の投皿を掘り䞋げたした そしお、残念な開発者でさえあるようなクラりドがありたす。そしお、先週末の終わりたでに発芋したした これ 

読むのが面倒な人のために Fragment.setRetainInstanceはFragmentManagerを䜿甚しおFragmentが砎壊されるのを防ぎたす-それはすべお倧䞈倫です。 しかし、䜕らかの理由で、開発者の1人がmFragmentManager = null;ずいう行を远加したした。 、およびFragment実装のみ-Activityがすべお問題ない理由です

なぜ、なぜ、そしおどのように刀明したかは、未回答になる興味深い質問です。 この1行のバグは2.5バヌゞョンでも続きたす。 前のリンク 怠け者の堎合 は、 リフレクションの回避策に぀いお説明しおいたす。 残念ながら、今のずころこれが問題を解決する唯䞀の方法ですもちろん、゜ヌスコヌドをプロゞェクトに完党にコピヌするこずは別ずしお。 問題自䜓は、 バグトラッカヌで詳现に説明されおいたす。



psタむムマシンを売らないwon・_├┬┎┬┎



5. RxJava  observeOnずsubscribeOnの違い



おそらく、私は最も単玔なものであるず同時に最も重芁なものから始めたす。

Rxを起動したばかりのずき、これらの方法の違いは私には完党に䞍明瞭でした。 論理的に、 subscribeOnは、 subscribeが呌び出されるスケゞュヌラを倉曎したす 。 しかし... ...別のロゞックの芳点では、 SubscriberはObserverを継承したすが、 Observerは䜕をしたすか おそらく芳察する 。 そしおここに認知的䞍協和音がありたした。 グヌグルも、 スタックオヌバヌフロヌも、 公匏のビヌ玉でさえ、明快さをもたらしたせんでした。 しかし、もちろん、そのような知識は非垞に重芁であり、 Schedulersで1週間たたは2぀のミスを犯した埌、それだけで生たれたした。

私はよく友人からこの質問を聞き、時にはさたざたなフォヌラムで出くわしたす。そのため、結果を心配せずに、「反応する」、たたはこれらの挔算子を単玔に盎感的に䜿甚する人の説明を次に瀺したす。

コヌド
 Observable.just(null) .doOnNext(v0id -> Log.i("TAG", "0")) //  : computation .observeOn(Schedulers.newThread()) .doOnNext(v0id -> Log.i("TAG", "1")) //  : newThread .observeOn(Schedulers.io()) // io .doOnNext(v0id -> Log.i("TAG", "2"))  : io .subscribeOn(Schedulers.computation()) .subscribe(v0id -> Log.i("TAG", "3")); // -  : io
      
      







私自身の経隓から最も玛らわしいこずは、 ReactiveXがスロヌガン「すべおはストリヌムです」で前進しおいるこずだず思いたす 。 その結果、新芏参入者は、各挔算子がそれに続く挔算子のみに圱響し、ストリヌム党䜓には圱響しないこずを期埅しおいたす。 しかし、これはそうではありたせん。 たずえば、 startWithはストリヌムの開始に圱響し、 finallyDoはストリヌムの終了に圱響したす。

Rx゜ヌスコヌドでの名前に぀いおは、デヌタはObservableクラスではなく突然ですか、 OnSubscribeクラスによっお生成されるこずがわかりたす。 これがsubscribeOn挔算子の玛らわしい名前の由来であるず思いたす。

ちなみに、 Frodoのログ蚘録に慣れおおくように、初心者や経隓豊富な愛奜家であっおも匷くお勧めしたす 。 Rxコヌドの借方蚘入は䟝然ずしお䜜業であるため、時間を倧幅に節玄しおください。



6. RxJava  挔算子ずトランスフォヌマヌ



状況


Rxコヌドが倧きくなるこずがよくあるので、どうにかしおそれを枛らしたいず思いたす。 チェヌンの圢でメ゜ッドを呌び出す方法は 、はい、良いですが、その再利甚はれロです-あなたは小さなこずなどを行うすべおの同じメ゜ッドを呌び出す必芁があるたびに など

このようなニヌズに盎面した初心者は、OOPの芳点から考え始め、すべおが本圓に悪い堎合は静的メ゜ッドを䜜成し、呌び出しのチェヌンの始たりをラップしたす。 時間内にこのアプロヌチを廃止しなければ、 Observableごずに3〜4個のラッパヌになりたす。

実際の補品のいずれかの実際のコヌド
 RxUtils.HandleErrors( RxUtils.FireGlobalEvents( RxUtils.SaveToCaches( Observable.defer(() -> storeApi.getAll(filter)).subscribeOn(Schedulers.io()), caches) , new StoreDataLoadedEvent() ) ).subscribe(storeDataObserver);
      
      







将来的には、これはコヌドが䜕をしおいるのかを理解したい人や䜕かを倉曎したい人に倚くの問題をもたらすでしょう。



そしお今䜕


チェヌンメ゜ッドは読みやすいため、優れおいたす。 できるだけ早く独自の挔算子ずトランスを䜜成する方法を孊ぶこずをお勧めしたす。 思ったより簡単です。 Operatorはデヌタナニットたずえば、 onNextを䞀床に1回呌び出すで動䜜し、 TransformerはObservable自䜓を倉換するこずを理解するこずが重芁ですここでは、通垞のmap/ doOnNextなどを1぀の党䜓に結合できたす。



すべお子䟛向けのゲヌムで終了したした。 溝に移りたしょう。



7. RxJava  サブスクリプションの実装におけるカオス



状況


だから、あなたは反応的です あなたはそれを詊しおみたした、あなたはそれを奜きでした、あなたはもっず欲しいです Rxですべおのテスト項目をすでに曞きたした。 Rxでホヌムプロゞェクトを曞き換えおいたす。 Rxにあなたの猫を教えたす。 そしお今、Grailを䜜成するずきがきたした。Rxでアヌキテクチャ党䜓を構築するためです。 あなたは準備ができおいたす、あなたは頻繁に、そしおだらしないように呌吞し、そしお...始めたす... moooey preeelst



なんで


残念ながら、䞊蚘は私のためです。 Rxのパワヌに非垞に驚いたので、アヌキテクチャを曞くためのすべおのアプロヌチを完党に再考するこずにしたした。 MVP + Rxを䜿甚しおMVVMを再発明しようずしたず蚀えたす。

ただし、初心者の最倧の間違いは認めたす。Rxを理解したず刀断したした。

それをよく理解するには、いく぀かのRxアプリケヌションを曞くだけでは䞍十分です 。 クリックずゞャンプを3぀の異なる゜ヌスからの写真、ビデオ、テストデヌタにリンクするよりもタスクが耇雑になるず、 バックプレッシャヌなどの突然の問題が発生したす。 そしお、 背圧を知っおいるず決めるず、あなたはProducerに぀いお䜕も知らないこずに気付くでしょう通垞のドキュメントさえもありたせん...䜕か私は逞れたすそしお蚘事の終わりに、それはなぜ明らかになりたす。

䞀般に、問題の本質はロゞックにあり、実際に利甚可胜なものずは逆になりたす。

リスニングは通垞どうですか
 //... data.registerListener(listener); // data.mListener == listener //... data.unregisterListener(); // data.mListener == null
      
      







぀たり、デヌタ゜ヌスにはリスナヌぞの参照が栌玍されたす。

しかし、Rxではどうなりたすか 慎重に、今では断片は少し田舎のコヌドになりたす

observer.unsubscribe 500ミリ秒で


コヌド
 Observable.interval(300, TimeUnit.MILLISECONDS).map(i -> "t1-" + i).subscribe(observer); l("interval-1"); Observable.interval(330, TimeUnit.MILLISECONDS).map(i -> "t2-" + i).subscribe(observer); l("interval-2"); Observable.timer(500, TimeUnit.MILLISECONDS).map(i -> "t3-" + i).subscribe(ignored -> observer.unsubscribe());
      
      







結果
 interval-1 interval-2 t1-0 t2-0
      
      







これが最も期埅される結果だず思いたす。 ありすべおずすべおを再䜜成する方法。



500msでsubscription1.unsubscribe


次に、 Subscriberからではなく、 Subscriptionからサブスクラむブを解陀しおみたしょう。 論理的な芳点から、 サブスクリプションはObserverずObservableを11ずしおバむンドし、䜕かから遞択的にサブスクリプションを解陀できるようにする必芁がありたすが、...

コヌド
 Subscription subscription1 = Observable.interval(300, TimeUnit.MILLISECONDS).map(i -> "t1-" + i).subscribe(observer); l("interval-1"); Observable.interval(330, TimeUnit.MILLISECONDS).map(i -> "t2-" + i).subscribe(observer); l("interval-2"); Observable.timer(500, TimeUnit.MILLISECONDS).map(i -> "t3-" + i).subscribe(ignored -> subscription1.unsubscribe());
      
      







結果
 interval-1 interval-2 t1-0 t2-0
      
      







...突然、結果はたったく同じになりたす。 Rxを初めお知ったずき、これに぀いおは知りたせんでしたが、それがうたくいくず思っお長い間同じようなアプロヌチを䜿甚しおいたした。 実際には、 SubscriberはObserverむンタヌフェむスず... Subscriptionを実装しおいたす。 ぀たり 私たちが持っおいるサブスクリプションは同じオブザヌバヌです ここにそのようなタヌンがありたす。



Observable.deferおよびObservable.fromCallable


deferは、 Rx  Observable.flatMapず同等で最もよく䜿甚される挔算子の1぀だず思いたす。 そのタスクは、 subscribeが呌び出されるたでObservableデヌタの初期化を延期するこずです。 詊しおみたしょう

コヌド
 Observable.defer(() -> Observable.just("s1")).subscribe(observer); l("just-1"); Observable.defer(() -> Observable.just("s2")).subscribe(observer); l("just-2"); observer.unsubscribe(); Observable.defer(() -> Observable.just("s3")).subscribe(observer); l("just-3");
      
      







結果
 s1 just-1 s2 just-2 s3 just-3
      
      







「そしお䜕 予期しないこずはありたせん」ずあなたは蚀いたす。 「おそらく」ず答えたす。

しかし、 Observable.justの䜜成にうんざりしおいる堎合はどうでしょうか Rxには答えがありたす。 グヌグルで簡単に怜玢するず、 Observable.fromCallableメ゜ッドが芋぀かりたす。これにより、 Observableではなく通垞のラムダを延期できたす。 私達は詊みたす

コヌド
 Observable.fromCallable(() -> "z1").subscribe(observer); l("callable-1"); Observable.fromCallable(() -> "z2").subscribe(observer); l("callable-2"); observer.unsubscribe(); Observable.fromCallable(() -> "z3").subscribe(observer); l("callable-3");
      
      







結果泚意画面から子䟛ずハムスタヌを削陀したす
 z1 callable-1 callable-2 callable-3
      
      







異なる゜ヌスデヌタでのみ同じこずをするメ゜ッドですが、そのような違いがあるように思われたす。 この結果で最もわかりにくい論理的に考えるずのは、 z1-z2-callableではないこずです これたでに説明したすべおを信じおいる堎合。぀たり、 z1-callable ...です。 問題は䜕ですか



事実は...


ここたでがポむントです。 実際、倚くの挔算子はさたざたな方法で蚘述されおいたす。 次のonNextの前の誰かがSubscriberサブスクリプションをチェックし、誰かが発行埌にチェックしたすが、最埌のonNextたで、そしお誰かが前埌にチェックしたす。 これにより、予想される結果に混乱が生じたす。 しかし、これでもObservable.fromCallableの動䜜を説明しおいたせん。

Rx内には、 SafeSubscriberクラスがありたす。 これはたさに、メむンのRxコントラクトを担圓するクラスです「 onErrorの埌、 onNextはなくなり、 サブスクリプション解陀などが発生する」など。 そしお、それをオペレヌタヌで䜿甚する必芁があるか SafeSubscriber かは、どこにも登録されおいたせん。 䞀般に、 Observable.fromCallableは通垞のsubscribe を呌び出したす。したがっお、 SafeSubscriberは暗黙的に䜜成され、 発行埌にunsubscribe が発生したすが、 Observable.deferはunsafeSubscribeを呌び出したす。 したがっお、実際には突然このObservable.deferは悪いものであり、 Observable.fromCallableではありたせん。



8. RxJava 手動でのサブスクラむブ解陀/サブスクラむブの代わりにrepeatWhen



状況


X秒ごずにデヌタを曎新する必芁がありたす。 もちろん、叀いデヌタをダりンロヌドするたで、新しいデヌタをダりンロヌドするこずはできたせんこれは、ラグ、バグ、およびその他の䞍正のために可胜です。 どうする

そしお、すべおが応答しお始たりたす Observable.interttleずObservable.throttleたたはAtomicBoolean 、そしお䞀郚は手動unsubscribe を介しおそれを行うこずさえできたす。 実際、すべおがはるかに単玔です。



解決策


Rxにはすべおの堎合に挔算子があるようです。 だから今です。 あなたのためにすべおを行うrepeatWhenメ゜ッドがありたす-指定された間隔でObservableに再サブスクラむブしたす

RepeatWhenの䟋
 Log.i("MY_TAG", "Loading data"); Observable.defer(() -> api.loadData())) .doOnNext(data -> view.setDataWithNotify(data)) .repeatWhen(completed -> completed.delay(7_777, TimeUnit.MILLISECONDS)) .subscribe( data -> Log.i("MY_TAG", "Data loaded"), e -> {}, v0id -> Log.i("MY_TAG", "Loading data")); // "Loading data" -   ; "Data loaded" -    ~8 .
      
      







唯䞀のマむナス点は、最初はこの方法がどのように機胜するかが完党に明確ではないこずです。 しかし、い぀ものように、 repeatWhen/ retryWhenに関する良い蚘事がありたす。



再詊行するずき


ちなみに、 repeatWhenのほかに、同じこずをするretryWhenもありたすが、 onErrorのためです。 ただし、 repeatWhenずは異なり、 retryWhenが圹立぀状況は非垞に具䜓的です。 䞊蚘の堎合、おそらく远加するこずができたす。 ただし、䞀般的には、 Rx Plugins / Hooksを䜿甚しお、目的の゚ラヌのためにグロヌバルハンドラヌをハングさせる方が適切です。 これにより、゚ラヌが発生した堎合にObservableを再サブスクラむブできるだけでなく、ナヌザヌに通知するこずもできたすたずえば、 SocketTimeoutExceptionに䌌たようなものを䜿甚したす。



远加。 RxJava 16



そしお最埌に、それが私がキュベットに぀いお曞き始めた理由です。 私が人生の2週間を費やしたが、どのような...魔法がそこに起こっおいるのかただわからないずいう問題...しかし、順番に芋おみたしょう。



状況


認蚌画面を䜜成し、誀っお入力されたフィヌルドをチェックし、3番目の゚ラヌごずに特別な譊告を発行する必芁がありたす。

タスク自䜓は難しくないため、 Rxの 「テストサむト」ずしお遞択したした。 サヌバヌからデヌタをダりンロヌドするだけのビゞネスずは異なるビゞネスでRxがどのように動䜜するのかを確認したす。

そのため、コヌドは次のようになりたした。

ログむン゚ラヌ凊理コヌド
 PublishSubject<String> wrongPasswordSubject = PublishSubject.create(); /*...*/ wrongPasswordSubject .compose(IndexingTransformer.Create()) .map(indexed -> String.format(((indexed.index % 3 == 0) ? "GREAT ERROR" : "Simple error") + " #%d : %s", indexed.index, indexed.value)) .observeOn(AndroidSchedulers.mainThread()) .subscribe(message -> getView().setMessage(message));
      
      







ボタン凊理コヌド[サむンむン]
 private void setSignInAction() { getView().getSignInButtonObservable() .observeOn(AndroidSchedulers.mainThread()) .doOnNext((v) -> getView().setSigningInState()) //    .observeOn(Schedulers.newThread()) .withLatestFrom(formDataSubject, (v, formData) -> formData) .map(formData -> auth(formData.login, formData.password)) // .   WrongLoginOrPassException .lift(new SuppressErrorOperator<>(throwable -> wrongPasswordSubject.onNext(throwable.getMessage()))) //      .compose(new UnObservableTransformer<>()) //       flatMap().      .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> getView().setSignedInState(user)); // happy end }
      
      







私たちは、 Rxスタむルのコヌドに察する䞻匵を延期したす。すべおが悪い、私はそれを自分で知っおいたす。 それはそうではなく、ずっず前に曞かれたした。

したがっお、 getView。GetSignInButtonObservableは、 [Sign In]ボタンをクリックしたずきにRxAndroidから受信したObservableを返したす。 これは泚目に倀するものです 。぀たり、 完了するこずはできたせん。 むベントはそれから始たり、 マップを通過したす。ここで認蚌が行われ、さらにチェヌンに沿っお進みたす。 ゚ラヌが発生した堎合、カスタムオペレヌタヌぱラヌをキャッチし、それ以䞊芋逃したせん。

SuppressErrorOperator
 public final class SuppressErrorOperator<T> implements Observable.Operator<T, T> { final Action1<Throwable> errorHandler; public SuppressErrorOperator(Action1<Throwable> errorHandler) { this.errorHandler = errorHandler; } @Override public Subscriber<? super T> call(final Subscriber<? super T> subscriber) { return new Subscriber<T>(subscriber) { @Override public void onCompleted() { subscriber.onCompleted(); } @Override public void onError(Throwable e) { errorHandler.call(e); //  ,    } @Override public void onNext(T t) { subscriber.onNext(t); } }; } }
      
      







だから問題は。 このコヌドの䜕が問題になっおいたすか

圌らがこれに぀いお私に尋ねたら、私は今でも答えたす「すべおは倧䞈倫です。」 Subscriptionを保存する堎所がないため、メモリリヌクを陀きたす。 はい、 onNextのみがsubscribeで䞊曞きされたすが、他のメ゜ッドは呌び出されたせん。 すべお順調です、私たちは取り組んでいたす。



痛み


ネクタむ


そしお、ここから最も奇劙なこずが始たりたす。 コヌドは本圓に機胜したす。 しかし、私は现心の泚意を払っおいるので、承認ボタンをクリックするこずにしたした...䜕床も。 そしお、突然、5番目の "GREAT ERROR"の埌、䜕らかの理由で認蚌の進行状況バヌ setSigningInStateで蚭定が機胜しなかったこずを発芋したしたこの機胜は[サむンむン]ボタンもオフにしたす。

「うヌん」ず思う。 UIを担圓するFragmentの関数を再確認したした突然、䜕かが挿入されたした。 auth関数を確認したした。テストのタむムアりトを蚭定しおいる可胜性がありたす。 いや 倧䞈倫です。

それから、私はそれがフロヌのレヌスであるず決めたした。 私はそれを再び開始し、もう䞀床チェックしたした...たさに5「GREAT ERROR」ず無限のプログレスバヌの停滞。 そしお緊匵した。 再び起動し、その埌たすたす。 たさに5 5番目の「GREAT ERROR」の盎埌に、ボタンが抌しおも反応しなくなるず、進行状況バヌが回転しお無音になりたす。

「わかりたした」、「 setSigningInStateを削陀したす。」 Androidは人ず遊ぶのが倧奜きです。 突然、 SDKの䜕かが壊れたした。唯䞀のこずは、ボタンをもう䞀床クリックするこずができず、そのハンドラヌが機胜しないこずだけではありたせん。」 いや 助けにはなりたせんでした。

この瞬間たでに、私はすでに非垞に緊匵しおいたした。 LogCatは空で、゚ラヌはなく、アプリケヌションは動䜜しおおり、ハングしおいたせんでした。 ハンドラヌはもう凊理したせん。



分析


タスク自䜓が私を欺いたこずが刀明したした。 「GREAT ERROR」の数を数えたしたが、実際にはボタンを抌した数を数えなければなりたせんでした。 正確に16。数倀は倉曎されたしたが、状況は倉わりたせん。

したがっお、䞍芁なものをすべお削陀した埌の次の詊みのコヌドは次のずおりです。

doOnNextにログがあるコヌド
 private void setSignInAction() { getView().getSignInButtonObservable() .observeOn(AndroidSchedulers.mainThread()) .doOnNext((v) -> l("1")) .observeOn(Schedulers.newThread()) .doOnNext((v) -> l("2")) .map(v -> { throw new RuntimeException(); }) .lift(new SuppressErrorOperator<>(throwable -> wrongPasswordSubject.onNext(throwable.getMessage()))) .doOnNext((v) -> l("3")) .observeOn(AndroidSchedulers.mainThread()) .doOnNext((v) -> l("4")) .subscribe(user -> runOnView(view -> view.setTextString("ON NEXT"))); }
      
      







そしお、状況はさらに奇劙になりたした。 1〜15回のクリックが正垞に行われ、数字の「1」ず「2」が衚瀺されたしたが、16回目のログの最埌の行は「1」でした。 ゚ラヌゞェネレヌタヌに到達したせんでした

「だから、それは䟋倖に関するものではないのでしょうか」ず思いたした。 throw new RuntimeExceptionをreturn nullおよび...で眮き換え、すべおが機胜し、クリックした回数に関係なく4桁すべおが衚瀺されたすすべおがフリヌズするこずを期埅しお100回以䞊クリックしたしたが、いいえ。

この瞬間たでに、2日目たたは3日目はすでに私の苊痛ずその時たでに持っおいたすべおのこずでした









来週、私は完党に手がかりを探すためにReactiveXの公匏りェブサむトを調べたした。私はgithubのRxJavaリポゞトリ、たたはそのwiki を調べたしたが、ただ答えが芋぀からなかったため、必死のステップに決め、...「pokeメ゜ッド」を䜿い始めたした。

できる限りのこずを詊しおみお、最終的に問題を解決したものを芋぀けたしたonBackpressureBuffer。䜕が背圧に説明りィキRxJava'vskogoリポゞトリは、ず私が蚀ったように、それは私が、怜玢時に読み取られおいたしたが、魔法はただ魔法で残っおいたした。

知らない人のために。背圧問題オペレヌタヌが前のオペレヌタヌからのデヌタを凊理する時間がないずきに発生したす。最も顕著な䟋はzipです。たず、オペレヌタは、その芁玠1毎に生成した堎合分、および秒-あたり1時間秒、次にゞッパヌが屈曲あろう。onBackpressureBuffer -挔算子によっお生成された党時間のすべおの倀を栌玍する配列を暗黙的に入力したす。したがっお、zipは意図したずおりに動䜜したすただし、最終的にOutOfMemoryExceptionを取埗したす。

それから質問に応じお、なぜonBackpressureBufferが圹立ったのでしょうか私は䞡方の方法でプログラムを実行したした。私もタむマヌをクリックしおみたした[サむンむン] 1分に1回だけですたあ、気にしないでください。突然Flashになり、クリックが速すぎたすか。もちろん、これは圹に立ちたせんでした。



ファむナル


それでも結局、observeOnの時点でコヌドが死んでいるこずに気付きたした。 「そしお、ここはどちら偎ですか」ずあなたは尋ねたす。 「¯\ _ツ_ /¯」-答えたす。

そのコヌド、コヌドonBackpressureBufferおよびObservable構造党䜓を研究するのに倚くの時間がかかりたした。それから、OnSubscribeクラス、Producer、その他の興味深いこずを孊びたした...しかし、これだけでは手がかりが埗られたせんでした。Rxの゜ヌスを完党に把握したず蚀っおいるわけではありたせん。いや、これはあたりにもクヌルですが、できる限り-圹に立たず、さらに深く掘るのは本圓に簡単ではありたせん。

もちろん、私はstackoverflowに関する質問をしたしたが、返事はありたせんでした。

onBackpressureBufferを非垞に迅速に発芋したずいう事実にもかかわらず、この溝には玄2週間かかりたしたしかし、問題の原因を理解せずに、誰が問題を解決するものを䜿甚したすか。



あなたの珟圚の経隓を䜿甚しお、仮定したすobserveOnを生成し、サブスクラむバ -obortku䞊で、私の加入者ずする堎合がありException'yは、圌らがラッパヌに蓄積する契玄のために䟋倖だれが16存圚するこずが予想されないようにするこずを、1でなければなりたせん。 17回目のクリックが発生するず、observeOnはisUnsubscribedをチェックし、それはただある真、誰も入れたせん。 しかし、これは私の掚枬です。

マゞックナンバヌ16に぀いお-䞀定の倧きさである背圧バッファ甚Android'a。通垞のJavaの堎合、 128であり、おそらくこの゚ラヌに぀いお私は決しお知らないでしょう。数字の16がアレむのサむズに関係しおいる可胜性が高いず掚枬する䟡倀がありたしたが、私は数字の5から始めたので、たったく考えおいたせんでした。番号16ぞの移行時たでに、2 + 2 = 17であるこずはすでに確信しおいたした。

そしお、最も魔法を远加した最埌のものはSuppressErrorOperatorです。最初に゚ラヌが無芖されなかった堎合、すぐにMissingBackpressureExceptionに気付くでしょうそしおその方向に疑問に思いたした。数日節玄できたす。実際には、ただ奇劙なこずが残っおいたすが、SuppressErrorOperatorはMissingBackpressureExceptionを含むすべおの゚ラヌを吞収するはずです。なぜならオペレヌタヌぱラヌのタむプをチェックしなかったため、すべおが動䜜し続けるはずです16回目の詊行[サむンむン]の埌、埌続のすべおが垞に無駄になるこずを陀く。



おわりに



したがっお、シリヌズの最埌の郚分は終了したした。批刀にもかかわらず、実際、私はRxのむディオムが非垞に奜きです-䞀床詊薬を詊しおみたら、ロヌダヌやその他ずは䜕の関係もありたせん。 Netflixのメンバヌはよくできおいたす。

ただし、Rxには欠点がありたす。デビュヌするのは難しく、䞀郚の挔算子は予枬できない結果をもたらしたす。これらの問題を説明する䟡倀はないず思いたす-蚘事のフロアはそれに぀いおです。しかし、私は䜕かを蚀いたす。 Rxは興味深いが挑戊的なものです。 Rx脳には倚くの皋床がありたす。これは、小さなアクションにのみ䜿甚できたすたずえば、Retrofit呌び出しの結果ずしお、ただし、Rxでアヌキテクチャ党䜓を構築し、巊右の耇雑な挔算子を䜿甚したり、倚数のサブスクリプションを監芖したりするこずができたす。 か぀お、ProducerでBackpressureを䜿甚しお画面を切り替えた埌、View Stateを埩元するコマンドのキュヌを䜜成しようずしたした。これを詊さないこずをお勧めしたす。匷く。䞀般に、無理をしないず非垞にクヌルになりたす。

Rxのための情報源を探しおいる人のために、より良いよりも䜕もありたせんすべおの事業者ずの公匏りェブサむトCtrlキヌ+ F、今あなたには、いく぀かに぀いおのすべおを知っおいるスキャン、RxJava github'e䞊のwikiの最も初心者のための、および オンラむンオペレヌタヌのむンタラクティブな䟋。

psそしお、もしあなたが誰かが最埌のキュベットでどんな魔法が起こっおいるか知っおいるなら-あなたはコメント、PM、たたは他の堎所で歓迎されおいたす。幎末幎始よりも詳现を知っおうれしいです。



UPD突然の玄16 stackoverflowの䞊の質問蚪れakarnokdメむンkontribyuterovの1 RxJava圓然で述べたように、artemgapchenko。理由は、observonOn decople'it挔算子がそれ自䜓の前埌にあり、バックプレッシャヌバッファヌずしお機胜するためです。なぜなら䟋倖が発生した堎合、requestを呌び出さず、デヌタを単に「飲み蟌む」だけで、observeOnは最初に芁求されたものだけ、぀たり定数16を返したす。onBackpressureBufferは、最初にLong.MAX_VALUEを芁求するため、問題を解決したす。Akarnokdからの元の回答。



All Articles