是否可以在"取消订阅"之后在RxJava中运行后台操作?

时间:2018-02-06 17:46:21

标签: concurrency rx-java

是否可以在" unscription"之后在rxJava中运行后台操作?

Ex:我创建了一个由3个字符串组成的流:A,B,C和I为C引入了高延迟,并为A和B引入了相同的延迟。通过使用运算符first()我取消订阅速度太快C和C没有时间被执行所以取消描述杀死C.是否有一种简单的方法让C继续在后台运行???

    @Test
    public void test_69_b() throws Exception {
        List<String> intList = Arrays.asList("A", "B", "C");
        print("start");
        Observable test = Observable.from(intList)
                                    .flatMap(this::findWrapperS)
                                    .first();
        print("build finnished");
        test.subscribe(this::printAtSub);
        print("End");
        Sleeper.sleep(Duration.ofSeconds(4));
    }

    private Observable<String> findWrapperS(String id) {
        return Observable.just(id).doOnUnsubscribe(() -> {
            print("Wrapper <" + id + "> is released");
        })
                         .observeOn(Schedulers.io())
                         .flatMap(i -> Observable.fromCallable(() -> sendBackString(i)));
    }

    private String sendBackString(String string) {

        switch (string){
            case "C":
                Sleeper.sleep(Duration.ofMillis(1000));
                print("Hello " + string);
                return string;

            default:
                Sleeper.sleep(Duration.ofMillis(10));
                print("Hello " + string);
                return string;
        }
    }
Given logs
=================
18:19:47.806 [Test worker] INFO tests.TestRunner - Got: start
18:19:47.831 [Test worker] INFO tests.TestRunner - Got: build finnished
18:19:47.989 [Test worker] INFO tests.TestRunner - Got: End
18:19:47.994 [Test worker] INFO tests.Sleeper - Sleeping PT4S ms
18:19:47.998 [RxIoScheduler-2] INFO tests.Sleeper - Sleeping PT0.01S ms
18:19:47.999 [RxIoScheduler-3] INFO tests.Sleeper - Sleeping PT0.01S ms
18:19:47.999 [RxIoScheduler-4] INFO tests.Sleeper - Sleeping PT1S ms
18:19:48.009 [RxIoScheduler-3] INFO tests.TestRunner - Got: Hello B
18:19:48.010 [RxIoScheduler-3] INFO tests.TestRunner - TERMINAL EVENT --->: B
18:19:48.012 [RxIoScheduler-3] INFO tests.TestRunner - Got: Wrapper <A> is released
18:19:48.013 [RxIoScheduler-3] INFO tests.TestRunner - Got: Wrapper <C> is released
18:19:48.015 [RxIoScheduler-3] INFO tests.TestRunner - Got: Wrapper <B> is released
18:19:48.015 [RxIoScheduler-2] INFO tests.TestRunner - Got: Hello A
18:19:48.021 [RxIoScheduler-4] WARN tests.Sleeper - Sleep interrupted
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at tests.Sleeper.sleep(Sleeper.java:24)
    at tests.TestRunner.sendBackString(TestRunner.java:249)
    at tests.TestRunner.lambda$null$15(TestRunner.java:242)
    at rx.internal.operators.OnSubscribeFromCallable.call(OnSubscribeFromCallable.java:48)
    at rx.internal.operators.OnSubscribeFromCallable.call(OnSubscribeFromCallable.java:33)
    at rx.Observable.unsafeSubscribe(Observable.java:10211)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:250)
    at rx.internal.operators.OperatorMerge$MergeSubscriber.onNext(OperatorMerge.java:147)
    at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:77)
    at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:227)
    at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:228)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
18:19:48.023 [RxIoScheduler-4] INFO tests.TestRunner - Got: Hello C

2 个答案:

答案 0 :(得分:1)

如果您想要完成一个长期运行的observable,最简单的方法是引入share()运算符。它将有两个订阅者,一个用于确保进程完成的空订阅者,另一个用于获取first()项并取消订阅的订阅者。

与往常一样,您需要关注对象的生命周期,并在资源不再可行时释放资源。

// Hold on to long running subscriptions so we can release
// them at the proper time
CompositeSubscription longRunningSubscribers = new CompositeSubscription();
...
private Observable<String> findWrapperS(String id) {
    Observable<String> obs = Observable.just(id).doOnUnsubscribe(() -> {
        print("Wrapper <" + id + "> is released");
       })
      .observeOn(Schedulers.io())
      .flatMap(i -> Observable.fromCallable(() -> sendBackString(i)))
      .share();
      Subscription longTerm = obs
        .subscribe( ignored -> {}, error -> {} );
      longRunningSubscribers.add( longTerm );
      return obs;
}

现在,findWrapperS()返回一个可以订阅和取消订阅的observable,而不会停止长时间运行的操作。当操作终止时,观察者链资源将被释放。但是,您仍可能需要在处理结束时致电longRunningSubscribers.unsubscribe()以释放订阅的资源。如果一切都已经完成,那么它所做的就是释放少量内存,这不是一件坏事。

答案 1 :(得分:0)

@BobDalgleish如果我在第一个()之前尝试“cache()”会怎么样?:

@Test public void test_69_b() throws Exception {
    List<String> intList = Arrays.asList("A", "B", "C");
    print("start");
    Observable test = Observable.from(intList)
                                .flatMap(this::findWrapperS)
                                .cache();
                                .first();
    print("build finnished");
    test.subscribe(this::printAtSub);
    print("End");
    Sleeper.sleep(Duration.ofSeconds(4)); 
}

我觉得它与你的命题有点类似,意思是缓存保留了第二个订阅者发出的可观察序列。