使用" skipWhile"结合" repeatWhen"在RxJava中实现服务器轮询

时间:2016-01-22 09:55:01

标签: android networking retrofit rx-java long-polling

我真的很喜欢RxJava,它是一个很棒的工具,但有时它很难理解它的工作原理。 我们在Android项目中使用Retrofit和RxJava,并且有以下用例:

我需要轮询服务器,重试之间有一些延迟,而服务器正在做一些工作。服务器完成后,我必须提供结果。所以我用RxJava成功完成了它,这里是代码片段: 我用过" skipWhile"用" repeatWhen"

Subscription checkJobSubscription = mDataManager.checkJob(prepareTweakJob)
        .skipWhile(new Func1<CheckJobResponse, Boolean>() {

            @Override
            public Boolean call(CheckJobResponse checkJobResponse) {
                boolean shouldSkip = false;

                if (SHOW_LOGS) Logger.v(TAG, "checkJob, skipWhile, jobStatus " + checkJobResponse.getJobStatus());

                switch (checkJobResponse.getJobStatus()){
                    case CheckJobResponse.PROCESSING:
                        shouldSkip = true;
                        break;
                    case CheckJobResponse.DONE:
                    case CheckJobResponse.ERROR:
                        shouldSkip = false;
                        break;
                }
                if (SHOW_LOGS) Logger.v(TAG, "checkJob, skipWhile, shouldSkip " + shouldSkip);

                return shouldSkip;
            }
        })
        .repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {
            @Override
            public Observable<?> call(Observable<? extends Void> observable) {
                if (SHOW_LOGS) Logger.v(TAG, "checkJob, repeatWhen " + observable);
                return observable.delay(1, TimeUnit.SECONDS);
            }
        }).subscribe(new Subscriber<CheckJobResponse>(){
            @Override
            public void onNext(CheckJobResponse response) {
                if (SHOW_LOGS) Logger.v(TAG, "checkJob, onSuccess, response " + response);

            }

            @Override
            public void onError(BaseError error) {
                if (SHOW_LOGS) Logger.v(TAG, "checkJob, onError, canEditTimeline, error " + error);
                Toast.makeText(ChoseEditOptionActivity.this, R.string.NETWORK__no_internet_message, Toast.LENGTH_LONG).show();

            }

            @Override
            public void onCompleted() {
                if (SHOW_LOGS) Logger.v(TAG, "onCompleted");
            }
        });

代码工作正常:

当服务器响应该作业正在处理时,我返回&#34; true&#34;来自&#34; skipWhile&#34;链,原始的Observable等待1秒钟再次执行http请求。 这个过程一直重复,直到我返回&#34; false&#34;来自&#34; skipWhile&#34;链

以下是我不理解的一些事情:

  1. 我在&#34; skipWhile&#34;的文档中看到了它不会从原始的Observable发出任何东西(onError,onNext,onComplete),直到我返回&#34; false&#34;来自&#34;来电&#34;方法。因此,如果它没有发出任何东西,为什么&#34;重复当&#34;可观察到它的工作?它等待一秒钟并再次运行请求。是谁推出了它?

  2. 第二个问题是:为什么Observable来自&#34; repeatWhen&#34;我没有永远地跑,我的意思是为什么当我回来时它会停止重复&#34; false&#34;来自&#34; skipWhile&#34;?如果我返回&#34; false&#34;我在我的订阅者中成功登录。

  3. 在&#34; repeatWhile&#34;的文档中它说最终我打电话给&#34; onComplete&#34;在我的订阅者中,但是&#34; onComplete&#34;永远不会被召唤。

  4. 如果我改变链接的顺序没有区别&#34; skipWhile&#34;并且&#34;重复当&#34;。那是为什么?

  5. 我知道RxJava是开源的,我只能阅读代码,但正如我所说 - 它真的很难理解。

    感谢。

1 个答案:

答案 0 :(得分:14)

我之前没有和repeatWhen合作,但这个问题让我很好奇,所以我做了一些研究。

skipWhile 确实会发出onErroronCompleted,即使它之前永远不会返回true。因此,每次repeatWhen发出checkJob()时都会调用onCompleted。这回答了问题#1。

其余问题是基于错误的假设。您的订阅永远在运行,因为您的repeatWhen永远不会终止。那是因为repeatWhen是比你意识到的更复杂的野兽。每当Observable从源获取null时,onCompleted就会发出onCompleted。如果您接受并返回delay则结束,否则如果您发出任何内容则会重试。由于null仅接受发射并延迟发射,因此它总是再次发出onCompleted。因此,它不断重新订阅。

然后,#2的答案是它永远在运行;您可能正在执行此代码之外的其他操作来取消订阅。对于#3,你永远不会得到takeUntil,因为它永远不会完成。对于#4,订单并不重要,因为您无限期地重复。

现在的问题是,你如何得到正确的行为?它就像使用skipWhile而不是Observable<Boolean> source = ...; // Something that eventually emits true source .repeatWhen(completed -> completed.delay(1, TimeUnit.SECONDS)) .takeUntil(result -> result) .filter(result -> result) .subscribe( res -> System.out.println("onNext(" + res + ")"), err -> System.out.println("onError()"), () -> System.out.println("onCompleted()") ); 一样简单。这样,您不断重复直到获得所需的结果,从而在您希望结束时终止该流。

这是一个代码示例:

source

在此示例中,true正在发出布尔值。我每1秒重复一次,直到源发出result。我一直在等true false。我会过滤掉true的所有通知,因此订阅者在<EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/bTextUrl" android:layout_alignParentTop="true" android:layout_alignParentStart="true" android:layout_alignParentEnd="true" android:background="#faf6f6" android:layout_marginTop="60dp" android:nextFocusDown="@+id/bSendUrl"> <requestFocus></requestFocus> </EditText> <Button android:layout_width="@dimen/latimea_button_send_tv" android:layout_height="@dimen/inaltimea_button_send_tv" android:text="@string/button_send_android_tv" android:id="@+id/bSendUrl" android:background="@drawable/button_send_tv" android:layout_below="@+id/bTextUrl" android:layout_alignParentStart="true" android:layout_marginTop="35dp" /> </Button> using (var client = new HttpClient(HttpClientHandlerFactory.GetWindowsAuthenticationHttpClientHandler())) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); string base64SerializedToken = "SuperDuperLongBase64String_IMeanSuperDuper"; client.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SerializedToken); Uri baseUri = new Uri("http:www.mywebapiservice.com"); Uri destinationUri = new Uri(baseUri, "doSomething"); HttpResponseMessage response = client.PostAsJsonAsync(new Uri(new Uri(this._baseUri), destinationUri.ToString()).ToString(), accountName).Result; if (response.IsSuccessStatusCode) { returnItem = response.Content.ReadAsAsync<MyCustomReturnObject>().Result; } else { string errorMessage = response.Content.ReadAsStringAsync().Result; throw new InvalidOperationException(errorMessage); } } public static class HttpClientHandlerFactory { public static HttpClientHandler GetWindowsAuthenticationHttpClientHandler() { HttpClientHandler returnHandler = new HttpClientHandler() { UseDefaultCredentials = true, PreAuthenticate = true }; return returnHandler; } } 之前无法获取这些通知。