11

I am trying to invoke a method that returns void (Java primitive type). I would like to delay the invoking of a it by a predefined amount of milliseconds. I know this can be simply done using a Handler by I prefer not to use it.

I tried to do:

Observable.just(getView().setAttachments(attachments)).delay(50, TimeUnit.MILLISECONDS);

However, there is a compilation error, that:

Observable.just(java.lang.Void) cannot be applied to (void)

Is there another way? The reason I would like not to use a Handler is that the code is defined in Presenter (MVP pattern) and I would not like to use Android specific code in Java only class. I would prefer it to be a cold Observable, as I would not have to subscribe to it, just invoke the method only once.

R. Zagórski
  • 20,020
  • 5
  • 65
  • 90

4 Answers4

35

You can achieve this with defer, delay and doOnNext.

Observable.defer(() -> Observable.just(null)
            .delay(3, TimeUnit.SECONDS))
            .doOnNext(ignore -> LogUtils.d("You action goes here"))
            .subscribe();

In RxJava 2 you can use the following:

Completable.complete()
     .delay(3, TimeUnit.SECONDS)
     .doOnComplete(() -> System.out.println("Time to complete " + (System.currentTimeMillis() - start)))
     .subscribe();

Test code for Paul's version:

    @Test
public void testTimeToDoOnSubscribeExecution() {
    final long startTime = System.currentTimeMillis();
    System.out.println("Starting at: " + startTime);
    Subscription subscription = Observable.empty()
            .doOnSubscribe(() -> System.out.println("Time to invoke onSubscribe: " + (System.currentTimeMillis() - startTime)))
            .delay(1000, TimeUnit.MILLISECONDS)
            .subscribe();
    new TestSubscriber((rx.Observer) subscription).awaitTerminalEvent(2, TimeUnit.SECONDS);
}

Output:

Starting at: 1467280697232
Time to invoke onSubscribe: 122
MatBos
  • 2,380
  • 24
  • 34
  • Checked this answer and the othe one. This one works with any delay. Thank you. – R. Zagórski Jun 30 '16 at 10:10
  • what about memory leak ? – Jemshit Aug 01 '16 at 13:29
  • Can you elaborate? – MatBos Aug 01 '16 at 13:30
  • Have you come across a clean way to achieve this using RxJava 2 yet? – rosterloh Jan 24 '17 at 11:01
  • I have updated the answer so that it has the RxJava 2 version. It is just one of ways to achieve it, so experiment with it. – MatBos Jan 24 '17 at 17:21
  • 1
    @MatBos `Maybe` emitts 0 or 1, and using `empty()` it will call `onComplete()` immediately. But for this, `Completable.complete()` will suffice as it is designed to never emit an object, which is what you need here. – Eurig Jones Jun 13 '17 at 09:25
  • `Completable.complete()`(http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Completable.html#complete() ) and `Maybe.empty()`(http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Maybe.html#empty()) do the same thing. Both complete, without waiting or emitting anything. Stream type is different but it has no meaning in this case as nothing is transported in it. – MatBos Jun 13 '17 at 09:34
  • Yes that's correct. They do exactly the same. But `Maybe` is designed to emit 0 or 1, where we actually always want 0. So in terms of someone picking it up as a code snippet, they might be puzzled why `Maybe` is necessary. – Eurig Jones Jun 13 '17 at 09:43
  • 1
    You're right someone can possibly be misled at the first glance. I have changed the example to `Completable`, thanks. – MatBos Jun 13 '17 at 09:50
  • you can use Completable.complete even in RxJava 1 – D. Sergeev Jul 23 '18 at 13:52
3

Use simply

subscription = Observable.timer(1000, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(aLong -> whatToDo());

private void whatToDo() {
   System.out.println("Called after 1 second");
}
Raghav Sharma
  • 780
  • 10
  • 18
1

If you dont want to emit anything in your pipeline I dont see the point to use a pipeline, just to use the delay?.

Anyway, what you want to achieve does not has sense, you cannot create an Observable that does not emit anything.

But if you want to use it anyway you can always use an operator like doOnSubscribe

     @Test
   public void observableDoOnSubscribe() {
    Observable.empty()
              .delay(50, TimeUnit.MILLISECONDS
              .doOnSubscribe(() -> getView().setAttachments(attachments))
              .subscribe();
}

To test that delay works

        boolean onSubscribe = false;

@Test
public void observableDoOnSubscribe() {
    Subscription subscription = Observable.just(System.currentTimeMillis())
              .doOnSubscribe(() -> onSubscribe = true)
              .delay(1000, TimeUnit.MILLISECONDS)
              .filter(s -> onSubscribe)
              .subscribe(t-> System.out.println("Pipeline spend time:" + (System.currentTimeMillis()-t)));

    new TestSubscriber((Observer) subscription)
            .awaitTerminalEvent(2, TimeUnit.SECONDS);
}

More reactive examples here https://github.com/politrons/reactive

paul
  • 12,873
  • 23
  • 91
  • 153
  • I thought there is another way, but that also works. Thank you. – R. Zagórski Jun 30 '16 at 09:36
  • That code will not delay execution of anything. As the method name states(`doOnSubscribe`) the execution will happen upon subscription. Try putting a bigger delay on it and test it then. – MatBos Jun 30 '16 at 09:38
  • @MatBos Why did you said the delay is not applied to the doOnSubscribe?, I think your´re wrong, but could you please copy and run the second example that I paste?. It worked for me, delaying the pipeline 1 second + the pipeline spend time. – paul Jun 30 '16 at 09:46
  • Paul, sorry to bring that to you but your test code is broken. Look at the test code in my comment (did not want to post code in comments for readability sake). You are correct in one thing, pipeline took a bit more than 1 second to complete as you delayed it, but time to invoke `doOnSubscribe` it not tested there. – MatBos Jun 30 '16 at 09:59
  • I must say, that really @paul code does not work. The reason it suits in my case is that the delay is really low. Checked answer below and it works better, with any delay. – R. Zagórski Jun 30 '16 at 10:10
  • @MatBos you´re right about the delay was not apply to the doOnsubscribe, but I did not the guy want to delay before the action, I thought he want just delay the pipeline. – paul Jun 30 '16 at 10:18
  • @paul No worries, mistakes happen! – MatBos Jun 30 '16 at 10:21
0

If your thread related to Ui then

Completable.complete()
            .delay(100, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .doOnComplete(() -> tabLayout.getTabAt(selectableTab).select())
            .subscribe();
sudhakara k s
  • 217
  • 3
  • 8