-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
Changes from all commits
8df8e9a
f5e3b5b
3919547
d34372a
0959aa5
35cf457
7d397a7
b80de93
60fa754
6f18d8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
apply plugin: 'java' | ||
apply plugin: 'eclipse' | ||
apply plugin: 'idea' | ||
apply plugin: 'osgi' | ||
|
||
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 'org.robolectric:robolectric:2.1.1' | ||
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 Android Javadoc ${project.version}" | ||
} | ||
options.addStringOption('top').value = '<h2 class="title" style="padding-top:40px">RxJava Android</h2>' | ||
} | ||
|
||
jar { | ||
manifest { | ||
name = 'rxjava-android' | ||
instruction 'Bundle-Vendor', 'Netflix' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.*,*' | ||
} | ||
} | ||
|
||
test { | ||
testLogging { | ||
exceptionFormat "full" | ||
events "started" | ||
displayGranularity 2 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package rx.android.concurrency; | ||
|
||
import android.os.Handler; | ||
import android.os.Looper; | ||
import rx.Scheduler; | ||
|
||
/** | ||
* Schedulers that have Android specific functionality | ||
*/ | ||
public class AndroidSchedulers { | ||
|
||
private static final Scheduler MAIN_THREAD_SCHEDULER = | ||
new HandlerThreadScheduler(new Handler(Looper.getMainLooper())); | ||
|
||
private AndroidSchedulers(){ | ||
|
||
} | ||
|
||
/** | ||
* {@link Scheduler} which uses the provided {@link Handler} to execute an action | ||
* @param handler The handler that will be used when executing the action | ||
* @return A handler based scheduler | ||
*/ | ||
public static Scheduler handlerThread(final Handler handler) { | ||
return new HandlerThreadScheduler(handler); | ||
} | ||
|
||
/** | ||
* {@link Scheduler} which will execute an action on the main Android UI thread. | ||
* | ||
* @return A Main {@link Looper} based scheduler | ||
*/ | ||
public static Scheduler mainThread() { | ||
return MAIN_THREAD_SCHEDULER; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package rx.android.concurrency; | ||
|
||
import android.os.Handler; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.mockito.ArgumentCaptor; | ||
import org.robolectric.RobolectricTestRunner; | ||
import org.robolectric.annotation.Config; | ||
import rx.Scheduler; | ||
import rx.Subscription; | ||
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; | ||
|
||
/** | ||
* Constructs a {@link HandlerThreadScheduler} using the given {@link Handler} | ||
* @param handler {@link Handler} to use when scheduling actions | ||
*/ | ||
public HandlerThreadScheduler(Handler handler) { | ||
this.handler = handler; | ||
} | ||
|
||
/** | ||
* Calls {@link HandlerThreadScheduler#schedule(Object, rx.util.functions.Func2, long, java.util.concurrent.TimeUnit)} | ||
* with a delay of zero milliseconds. | ||
* | ||
* See {@link #schedule(Object, rx.util.functions.Func2, long, java.util.concurrent.TimeUnit)} | ||
*/ | ||
@Override | ||
public <T> Subscription schedule(final T state, final Func2<Scheduler, T, Subscription> action) { | ||
return schedule(state, action, 0L, TimeUnit.MILLISECONDS); | ||
} | ||
|
||
/** | ||
* Calls {@link Handler#postDelayed(Runnable, long)} with a runnable that executes the given action. | ||
* @param state | ||
* State to pass into the action. | ||
* @param action | ||
* Action to schedule. | ||
* @param delayTime | ||
* Time the action is to be delayed before executing. | ||
* @param unit | ||
* Time unit of the delay time. | ||
* @return A Subscription from which one can unsubscribe from. | ||
*/ | ||
@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(RobolectricTestRunner.class) | ||
@Config(manifest=Config.NONE) | ||
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); | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.