Rxjava: 2.x: Scheduler Callable result can't be null, setInitIoSchedulerHandler with trampoline

Created on 17 Mar 2017  路  2Comments  路  Source: ReactiveX/RxJava

RxJava version is 2.0.1

Not sure if it's a bug or my lack of knowledge. The issue I have happens when I try to return trampoline scheduler instead of io for my unit tests. An error occurs:
````
java.lang.ExceptionInInitializerError
at com.dariuszdeoniziak.charades.presenters.CategoryListPresenter.loadCategories(CategoryListPresenter.java:53)
at com.dariuszdeoniziak.charades.presenters.CategoryListPresenterTest.loadCategoriesCallsShowCategories(CategoryListPresenterTest.java:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.NullPointerException: Scheduler Callable result can't be null
at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39)
at io.reactivex.plugins.RxJavaPlugins.applyRequireNonNull(RxJavaPlugins.java:989)
at io.reactivex.plugins.RxJavaPlugins.initIoScheduler(RxJavaPlugins.java:213)
at io.reactivex.schedulers.Schedulers.(Schedulers.java:79)
... 31 more

Process finished with exit code 255
````

The loadCategories code is as follows, the exception is thrown at .subscribeOn(Schedulers.io()):

Single<List<Category>> categoriesSingle = Single.fromCallable(new Callable<List<Category>>() {

        @Override
        public List<Category> call() throws Exception {
            return modelInteractor.getCategories();
        }
    });

public void loadCategories() {
        view.showProgressIndicator();
        categoriesSingle
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new SingleObserver<List<Category>>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onSuccess(List<Category> value) {
                        if (value.isEmpty())
                            view.showEmptyList();
                        else
                            view.showCategories(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        view.showEmptyList();
                    }
                });
    }

Test just calls loadCategories method and mocks few things:

@Test
public void loadCategoriesCallsShowCategories() {
    when(presenter.modelInteractor.getCategories())
            .thenReturn(categories);
    presenter.loadCategories();
    verify(modelInteractor).getCategories();
    verify(view).showProgressIndicator();
    verify(view).showCategories(categories);
}

Last but not least, the place where I setInitIoSchedulerHandler is custom TestRunner:

package com.dariuszdeoniziak.charades.utils;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

import java.util.concurrent.Callable;

import io.reactivex.Scheduler;
import io.reactivex.android.plugins.RxAndroidPlugins;
import io.reactivex.functions.Function;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;

public class RxJavaTestRunner extends BlockJUnit4ClassRunner {

    public RxJavaTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);

        RxAndroidPlugins.reset();
        RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
            @Override
            public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
                return Schedulers.trampoline();
            }
        });

        RxJavaPlugins.reset();
        RxJavaPlugins.setInitNewThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
            @Override
            public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
                return Schedulers.trampoline();
            }
        });
        RxJavaPlugins.setInitIoSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
            @Override
            public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
                return Schedulers.trampoline();
            }
        });
    }
}

Is it possible to setInitIoSchedulerHandler as above?
Full project is available on my git: https://github.com/darekdeo/charades
I have currently replaced Schedulers.io with Schedulers.newThread on git, which works fine.

2.x Question

Most helpful comment

No. When you call setInitX you can't access the Schedulers from within because the callback itself executes on the Schedulers' class initialization and other Schedulers fields may not be ready. Use the plain setIoSchedulerHandler to override and later clear the custom scheduler.

All 2 comments

No. When you call setInitX you can't access the Schedulers from within because the callback itself executes on the Schedulers' class initialization and other Schedulers fields may not be ready. Use the plain setIoSchedulerHandler to override and later clear the custom scheduler.

It works, thank you. This was fast.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlew picture dlew  路  4Comments

midnight-wonderer picture midnight-wonderer  路  3Comments

yubaokang picture yubaokang  路  3Comments

gfx picture gfx  路  3Comments

aballano picture aballano  路  3Comments