たとえば、ユーザーがインターネットに接続しておらず、インターネットからデータを受信したい場合など、ネットワークに対して繰り返し要求を行う必要があります。 リクエストが表示されたら再送信してください。 ユーザーに何が起こったのかを説明し、リクエストを再度スローできるようにする特定のUIを表示することをお勧めします。 特に膨大な数のViewModelクラスがある場合、このようなロジックを追加するのは非常に困難です。 もちろん、各ViewModelクラスに再クエリロジックを実装できますが、これは便利ではなく、エラーが発生する可能性が非常に高くなります。
この処理を一度だけ行う方法はありますか?
幸いなことに、RxJava2とRetrofit2はこれを許可します。
Stackoverflowにはすでにいくつかのソリューションがあります。
1.独自のCallAdapterFactoryの作成(詳細はこちら )
2. PublishSubjectを使用してチェーンを繰り返します(詳細はこちら )
最初のソリューションはRxJava1を使用しますが、これは既に時代遅れであり、イベントの発生に反応することなく、チェーンを数回繰り返すだけです。 2番目のソリューションは適切ですが、各チェーンでretryWhen演算子を使用する必要があります。 そのため、2つのソリューションを1つにまとめました。
実装
簡単なプロジェクトを作成しましょう。 メイン画面に2つのタブを配置します。 それぞれに、APIによってロードされた要素の数を示すテキストが表示されます。 実行中にエラーが発生した場合、[再試行]ボタンのあるSnackBarを表示します。
BaseActivity、BaseFragment、BaseViewModelなどの基本クラスを定義します。これらは、1か所で要求を繰り返すロジックを実装し、このコードの重複を避けるために必要です。 BaseFragmentを拡張する2つのフラグメントを作成します。 配置された各フラグメントには独自のViewModelがあり、APIリクエストを個別に行います。 エラーが発生した場合、各リクエストが繰り返されることを示すために、これらのフラグメントを作成しました。 次に、CallAdapter.Factoryを拡張するRxRetryCallAdapterFactoryファクトリーを作成します。 その後、RxJava2CallAdapterFactoryのインスタンスを作成します。 すでにRetrofitライブラリにあるコードを複製したくないため、RxJava2CallAdapterにアクセスするにはこのインスタンスが必要です。 また、ファクトリのインスタンスを返す静的メソッドを作成しましょう。 以下のサンプルコード:
class RxRetryCallAdapterFactory : CallAdapter.Factory() { companion object { fun create() : CallAdapter.Factory = RxRetryCallAdapterFactory() } private val originalFactory = RxJava2CallAdapterFactory.create() override fun get(returnType : Type, annotations : Array<Annotation>, retrofit : Retrofit) : CallAdapter<*, *>? { val adapter = originalFactory.get(returnType, annotations, retrofit) ?: return null return RxRetryCallAdapter(adapter) } }
次に、CallAdapterインターフェイスを実装するRxRetryCallAdapterを作成し、CallAdapterインスタンスをコンストラクターに渡す必要があります。 実際、元のファクトリを返すRxJava2CallAdapterのインスタンスである必要があります。
次に、次のものを実装する必要があります。
- retryWhenステートメントは繰り返し機能の実装に使用されます
- エラーを処理するdoOnError()ステートメント。 BaseActivityで処理され、SnackBarをユーザーに表示するブロードキャストを送信するために使用されます。
- PublishSubjectは、チェーンに再署名するイベントトリガーとして使用されます。
- publishSubjectに適用する必要があるobserveOn(Schedulers.io())演算子(この行が追加されない場合、サブスクリプションはメインスレッドで発生し、NetworkOnMainThreadExceptionを取得します
- 最後のエラーのみが必要なので、PublishSubjectをFlowableに変換し、BackpressureStrategy.LATESTを設定します
注:PublishSubjectを提供するために、プロジェクト内のすべてのシングルトン依存関係を提供する単純なシングルトンクラスを作成しました。 実際のプロジェクトでは、おそらくDagger2のような依存性注入フレームワークを使用します。
class RxRetryCallAdapter<R>(private val originalAdapter : CallAdapter<R, *>) : CallAdapter<R, Any> { override fun adapt(call : Call<R>) : Any { val adaptedValue = originalAdapter.adapt(call) return when (adaptedValue) { is Completable -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } } is Single<*> -> { adaptedValue.doOnError(this::sendBroadcast) .retryWhen { AppProvider.provideRetrySubject().toFlowable(BackpressureStrategy.LATEST) .observeOn(Schedulers.io()) } } //same for Maybe, Observable, Flowable else -> { adaptedValue } } } override fun responseType() : Type = originalAdapter.responseType() private fun sendBroadcast(throwable : Throwable) { Timber.e(throwable) LocalBroadcastManager.getInstance(AppProvider.appInstance).sendBroadcast(Intent(BaseActivity.ERROR_ACTION)) } }
ユーザーが[再試行]ボタンをクリックすると、onNext PublishSubjectが呼び出されます。 その後、rxチェーンを再サブスクライブします。
テスト中
インターネットをオフにして、アプリケーションを実行します。 ロードされたアイテムの数は各タブでゼロであり、SnackBarはエラーを表示します。 インターネットをオンにして、Try Adainをクリックします。 数秒後、ロードされたアイテムの数が各タブで変わります。
誰かがそれを必要とするなら、ソースコードはここにある