Skip to content

Commit

Permalink
Add lazy loading of default main Scheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-tackage authored and JakeWharton committed Oct 7, 2016
1 parent fbc3c9d commit 79d0991
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/
package io.reactivex.android.plugins;

import java.util.concurrent.Callable;

import io.reactivex.Scheduler;
import io.reactivex.exceptions.Exceptions;
import io.reactivex.functions.Function;
Expand All @@ -21,39 +23,38 @@
* Utility class to inject handlers to certain standard RxAndroid operations.
*/
public final class RxAndroidPlugins {
private static volatile Function<Scheduler, Scheduler> onInitMainThreadHandler;

private static volatile Function<Callable<Scheduler>, Scheduler> onInitMainThreadHandler;
private static volatile Function<Scheduler, Scheduler> onMainThreadHandler;

public static void setInitMainThreadSchedulerHandler(Function<Scheduler, Scheduler> handler) {
public static void setInitMainThreadSchedulerHandler(Function<Callable<Scheduler>, Scheduler> handler) {
onInitMainThreadHandler = handler;
}

public static Scheduler initMainThreadScheduler(Scheduler scheduler) {
Function<Scheduler, Scheduler> f = onInitMainThreadHandler;
if (f == null) {
return scheduler;
public static Scheduler initMainThreadScheduler(Callable<Scheduler> scheduler) {
if (scheduler == null) {
throw new NullPointerException("scheduler == null");
}
try {
return f.apply(scheduler);
} catch (Exception e) {
throw Exceptions.propagate(e);
Function<Callable<Scheduler>, Scheduler> f = onInitMainThreadHandler;
if (f == null) {
return callRequireNonNull(scheduler);
}
return applyRequireNonNull(f, scheduler);
}

public static void setMainThreadSchedulerHandler(Function<Scheduler, Scheduler> handler) {
onMainThreadHandler = handler;
}

public static Scheduler onMainThreadScheduler(Scheduler scheduler) {
if (scheduler == null) {
throw new NullPointerException("scheduler == null");
}
Function<Scheduler, Scheduler> f = onMainThreadHandler;
if (f == null) {
return scheduler;
}
try {
return f.apply(scheduler);
} catch (Exception e) {
throw Exceptions.propagate(e);
}
return apply(f, scheduler);
}

/**
Expand All @@ -64,6 +65,34 @@ public static void reset() {
setMainThreadSchedulerHandler(null);
}

static Scheduler callRequireNonNull(Callable<Scheduler> s) {
try {
Scheduler scheduler = s.call();
if (scheduler == null) {
throw new NullPointerException("Scheduler Callable returned null");
}
return scheduler;
} catch (Throwable ex) {
throw Exceptions.propagate(ex);
}
}

static Scheduler applyRequireNonNull(Function<Callable<Scheduler>, Scheduler> f, Callable<Scheduler> s) {
Scheduler scheduler = apply(f,s);
if (scheduler == null) {
throw new NullPointerException("Scheduler Callable returned null");
}
return scheduler;
}

static <T, R> R apply(Function<T, R> f, T t) {
try {
return f.apply(t);
} catch (Throwable ex) {
throw Exceptions.propagate(ex);
}
}

private RxAndroidPlugins() {
throw new AssertionError("No instances.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,26 @@

import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Callable;

import io.reactivex.Scheduler;
import io.reactivex.android.plugins.RxAndroidPlugins;

/** Android-specific Schedulers. */
public final class AndroidSchedulers {

private static final class MainHolder {

static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));
}

private static final Scheduler MAIN_THREAD = RxAndroidPlugins.initMainThreadScheduler(
new HandlerScheduler(new Handler(Looper.getMainLooper())));
new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
return MainHolder.DEFAULT;
}
});

/** A {@link Scheduler} which executes actions on the Android main thread. */
public static Scheduler mainThread() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@
*/
package io.reactivex.android.plugins;

import io.reactivex.Scheduler;
import io.reactivex.android.testutil.EmptyScheduler;
import io.reactivex.functions.Function;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;

import io.reactivex.Scheduler;
import io.reactivex.android.testutil.EmptyScheduler;
import io.reactivex.functions.Function;

import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

@RunWith(RobolectricTestRunner.class)
Expand Down Expand Up @@ -55,7 +60,7 @@ public void mainThreadHandlerCalled() {
public void resetClearsMainThreadHandler() {
RxAndroidPlugins.setMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {
@Override public Scheduler apply(Scheduler scheduler) {
throw new RuntimeException();
throw new AssertionError();
}
});
RxAndroidPlugins.reset();
Expand All @@ -67,32 +72,90 @@ public void resetClearsMainThreadHandler() {

@Test
public void initMainThreadHandlerCalled() {
final AtomicReference<Scheduler> schedulerRef = new AtomicReference<>();
final AtomicReference<Callable<Scheduler>> schedulerRef = new AtomicReference<>();
final Scheduler newScheduler = new EmptyScheduler();
RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {
@Override public Scheduler apply(Scheduler scheduler) {
schedulerRef.set(scheduler);
return newScheduler;
}
});
RxAndroidPlugins
.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override public Scheduler apply(Callable<Scheduler> scheduler) {
schedulerRef.set(scheduler);
return newScheduler;
}
});

Scheduler scheduler = new EmptyScheduler();
Callable<Scheduler> scheduler = new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
throw new AssertionError();
}
};
Scheduler actual = RxAndroidPlugins.initMainThreadScheduler(scheduler);
assertSame(newScheduler, actual);
assertSame(scheduler, schedulerRef.get());
}

@Test
public void resetClearsInitMainThreadHandler() {
RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Scheduler, Scheduler>() {
@Override public Scheduler apply(Scheduler scheduler) {
throw new RuntimeException();
public void resetClearsInitMainThreadHandler() throws Exception {
RxAndroidPlugins
.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override public Scheduler apply(Callable<Scheduler> scheduler) {
throw new AssertionError();
}
});

final Scheduler scheduler = new EmptyScheduler();
Callable<Scheduler> schedulerCallable = new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
return scheduler;
}
});
};

RxAndroidPlugins.reset();

Scheduler scheduler = new EmptyScheduler();
Scheduler actual = RxAndroidPlugins.initMainThreadScheduler(scheduler);
assertSame(scheduler, actual);
Scheduler actual = RxAndroidPlugins.initMainThreadScheduler(schedulerCallable);
assertSame(schedulerCallable.call(), actual);
}

@Test
public void defaultMainThreadSchedulerIsInitializedLazily() {
Function<Callable<Scheduler>, Scheduler> safeOverride =
new Function<Callable<Scheduler>, Scheduler>() {
@Override public Scheduler apply(Callable<Scheduler> scheduler) {
return new EmptyScheduler();
}
};
Callable<Scheduler> unsafeDefault = new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
throw new AssertionError();
}
};

RxAndroidPlugins.setInitMainThreadSchedulerHandler(safeOverride);
RxAndroidPlugins.initMainThreadScheduler(unsafeDefault);
}

@Test
public void overrideInitMainSchedulerThrowsWhenSchedulerCallableIsNull() {
try {
RxAndroidPlugins.initMainThreadScheduler(null);
fail();
} catch (NullPointerException e) {
assertEquals("scheduler == null", e.getMessage());
}
}

@Test
public void overrideInitMainSchedulerThrowsWhenSchedulerCallableReturnsNull() {
Callable<Scheduler> nullResultCallable = new Callable<Scheduler>() {
@Override public Scheduler call() throws Exception {
return null;
}
};

try {
RxAndroidPlugins.initMainThreadScheduler(nullResultCallable);
fail();
} catch (NullPointerException e) {
assertEquals("Scheduler Callable returned null", e.getMessage());
}
}

}

0 comments on commit 79d0991

Please sign in to comment.