Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for scheduling on Android Handler threads #318

Merged
merged 10 commits into from
Aug 23, 2013
51 changes: 51 additions & 0 deletions rxjava-contrib/rxjava-android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'osgi'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically copied this file over from the Swing contrib module. There's a lot of cruft in here which I think can be remove or at least shared with the parent modules? I'm thinking about IDE project file generation and JavaDoc setup.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's probably the case, I have not yet attempted doing that as I'm not the most comfortable with Gradle.

sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6

dependencies {
compile project(':rxjava-core')
provided 'junit:junit-dep:4.10'
provided 'org.mockito:mockito-core:1.8.5'
provided 'com.pivotallabs:robolectric:1.2'
provided 'com.google.android:android:4.0.1.2'
}

eclipse {
classpath {
// include 'provided' dependencies on the classpath
plusConfigurations += configurations.provided

downloadSources = true
downloadJavadoc = true
}
}

idea {
module {
// include 'provided' dependencies on the classpath
scopes.PROVIDED.plus += configurations.provided
}
}

javadoc {
options {
doclet = "org.benjchristensen.doclet.DocletExclude"
docletpath = [rootProject.file('./gradle/doclet-exclude.jar')]
stylesheetFile = rootProject.file('./gradle/javadocStyleSheet.css')
windowTitle = "RxJava Javadoc ${project.version}"
}
options.addStringOption('top').value = '<h2 class="title" style="padding-top:40px">RxJava</h2>'
}

jar {
manifest {
name = 'rxjava-android'
instruction 'Bundle-Vendor', 'Netflix'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case SoundCloud wants to take ownership of the Android module, should probably update this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know enough about OSGI requirements to comment on how this behaves. This artifact as part of RxJava however will be on maven central under "com.netflix.rxjava" as that's the namespace on Maven, despite keeping all of the code namespaced to just "rx".

instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava'
instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package rx.android.testsupport;

import com.xtremelabs.robolectric.RobolectricConfig;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import org.junit.runners.model.InitializationError;

import java.io.File;

public class AndroidTestRunner extends RobolectricTestRunner {

public AndroidTestRunner(Class<?> testClass) throws InitializationError {
super(testClass, new RobolectricConfig(new File("src/test/resources")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package rx.android.testsupport;

public class R {
// resource stub for Robolectric
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package rx.concurrency;

import android.os.Handler;
import android.os.Looper;
import rx.Scheduler;

public class AndroidSchedulers {

private static final Scheduler MAIN_THREAD_SCHEDULER =
new HandlerThreadScheduler(new Handler(Looper.getMainLooper()));

public static Scheduler handlerThread(final Handler handler) {
return new HandlerThreadScheduler(handler);
}

public static Scheduler mainThread() {
return MAIN_THREAD_SCHEDULER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package rx.concurrency;

import android.os.Handler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import rx.Scheduler;
import rx.Subscription;
import rx.android.testsupport.AndroidTestRunner;
import rx.operators.AtomicObservableSubscription;
import rx.util.functions.Func2;

import java.util.concurrent.TimeUnit;

import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

/**
* Schedules actions to run on an Android Handler thread.
*/
public class HandlerThreadScheduler extends Scheduler {

private final Handler handler;

public HandlerThreadScheduler(Handler handler) {
this.handler = handler;
}

@Override
public <T> Subscription schedule(final T state, final Func2<Scheduler, T, Subscription> action) {
return schedule(state, action, 0L, TimeUnit.MILLISECONDS);
}

@Override
public <T> Subscription schedule(final T state, final Func2<Scheduler, T, Subscription> action, long delayTime, TimeUnit unit) {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
final Scheduler _scheduler = this;
handler.postDelayed(new Runnable() {
@Override
public void run() {
subscription.wrap(action.call(_scheduler, state));
}
}, unit.toMillis(delayTime));
return subscription;
}

@RunWith(AndroidTestRunner.class)
public static final class UnitTest {

@Test
public void shouldScheduleImmediateActionOnHandlerThread() {
final Handler handler = mock(Handler.class);
final Object state = new Object();
final Func2<Scheduler, Object, Subscription> action = mock(Func2.class);

Scheduler scheduler = new HandlerThreadScheduler(handler);
scheduler.schedule(state, action);

// verify that we post to the given Handler
ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
verify(handler).postDelayed(runnable.capture(), eq(0L));

// verify that the given handler delegates to our action
runnable.getValue().run();
verify(action).call(scheduler, state);
}

@Test
public void shouldScheduleDelayedActionOnHandlerThread() {
final Handler handler = mock(Handler.class);
final Object state = new Object();
final Func2<Scheduler, Object, Subscription> action = mock(Func2.class);

Scheduler scheduler = new HandlerThreadScheduler(handler);
scheduler.schedule(state, action, 1L, TimeUnit.SECONDS);

// verify that we post to the given Handler
ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
verify(handler).postDelayed(runnable.capture(), eq(1000L));

// verify that the given handler delegates to our action
runnable.getValue().run();
verify(action).call(scheduler, state);
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="rx.android.testsupport">

<application />

</manifest>
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ include 'rxjava-core', \
'language-adaptors:rxjava-jruby', \
'language-adaptors:rxjava-clojure', \
'language-adaptors:rxjava-scala', \
'rxjava-contrib:rxjava-swing'
'rxjava-contrib:rxjava-swing', \
'rxjava-contrib:rxjava-android'