Skip to content

Commit 56d76bb

Browse files
Merge pull request #1321 from dpsm/master
Ensuring Runnables posted with delay to a Handler are removed when unsub...
2 parents 473a567 + b5655d2 commit 56d76bb

File tree

2 files changed

+71
-27
lines changed

2 files changed

+71
-27
lines changed
Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Copyright 2014 Netflix, Inc.
3-
*
3+
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,8 +20,8 @@
2020
import rx.Scheduler;
2121
import rx.Subscription;
2222
import rx.functions.Action0;
23-
import rx.functions.Action1;
24-
import rx.subscriptions.BooleanSubscription;
23+
import rx.internal.schedulers.ScheduledAction;
24+
import rx.subscriptions.CompositeSubscription;
2525
import rx.subscriptions.Subscriptions;
2626
import android.os.Handler;
2727

@@ -34,7 +34,7 @@ public class HandlerThreadScheduler extends Scheduler {
3434

3535
/**
3636
* Constructs a {@link HandlerThreadScheduler} using the given {@link Handler}
37-
*
37+
*
3838
* @param handler
3939
* {@link Handler} to use when scheduling actions
4040
*/
@@ -46,47 +46,42 @@ public HandlerThreadScheduler(Handler handler) {
4646
public Worker createWorker() {
4747
return new InnerHandlerThreadScheduler(handler);
4848
}
49-
49+
5050
private static class InnerHandlerThreadScheduler extends Worker {
5151

5252
private final Handler handler;
53-
private BooleanSubscription innerSubscription = new BooleanSubscription();
53+
54+
private final CompositeSubscription compositeSubscription = new CompositeSubscription();
5455

5556
public InnerHandlerThreadScheduler(Handler handler) {
5657
this.handler = handler;
5758
}
5859

5960
@Override
6061
public void unsubscribe() {
61-
innerSubscription.unsubscribe();
62+
compositeSubscription.unsubscribe();
6263
}
6364

6465
@Override
6566
public boolean isUnsubscribed() {
66-
return innerSubscription.isUnsubscribed();
67+
return compositeSubscription.isUnsubscribed();
6768
}
6869

6970
@Override
7071
public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) {
71-
final Runnable runnable = new Runnable() {
72-
@Override
73-
public void run() {
74-
if (isUnsubscribed()) {
75-
return;
76-
}
77-
action.call();
78-
}
79-
};
80-
handler.postDelayed(runnable, unit.toMillis(delayTime));
81-
return Subscriptions.create(new Action0() {
82-
72+
final ScheduledAction scheduledAction = new ScheduledAction(action);
73+
scheduledAction.add(Subscriptions.create(new Action0() {
8374
@Override
8475
public void call() {
85-
handler.removeCallbacks(runnable);
86-
76+
handler.removeCallbacks(scheduledAction);
8777
}
88-
89-
});
78+
}));
79+
scheduledAction.addParent(compositeSubscription);
80+
compositeSubscription.add(scheduledAction);
81+
82+
handler.postDelayed(scheduledAction, unit.toMillis(delayTime));
83+
84+
return scheduledAction;
9085
}
9186

9287
@Override
@@ -95,5 +90,4 @@ public Subscription schedule(final Action0 action) {
9590
}
9691

9792
}
98-
9993
}

rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,26 @@
1818
import static org.mockito.Matchers.any;
1919
import static org.mockito.Matchers.eq;
2020
import static org.mockito.Mockito.mock;
21+
import static org.mockito.Mockito.never;
22+
import static org.mockito.Mockito.spy;
2123
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
2225

2326
import java.util.concurrent.TimeUnit;
2427

2528
import org.junit.Test;
2629
import org.junit.runner.RunWith;
2730
import org.mockito.ArgumentCaptor;
31+
import org.mockito.Matchers;
32+
import org.robolectric.Robolectric;
2833
import org.robolectric.RobolectricTestRunner;
2934
import org.robolectric.annotation.Config;
3035

36+
import rx.Observable;
3137
import rx.Scheduler;
3238
import rx.Scheduler.Worker;
39+
import rx.Subscriber;
40+
import rx.Subscription;
3341
import rx.functions.Action0;
3442
import rx.functions.Action1;
3543
import android.os.Handler;
@@ -75,4 +83,46 @@ public void shouldScheduleDelayedActionOnHandlerThread() {
7583
runnable.getValue().run();
7684
verify(action).call();
7785
}
86+
87+
@Test
88+
public void shouldRemoveCallbacksFromHandlerWhenUnsubscribedSubscription() {
89+
final Handler handler = spy(new Handler());
90+
final Observable.OnSubscribe<Integer> onSubscribe = mock(Observable.OnSubscribe.class);
91+
final Subscription subscription = Observable.create(onSubscribe).subscribeOn(
92+
new HandlerThreadScheduler(handler)).subscribe();
93+
94+
verify(onSubscribe).call(Matchers.any(Subscriber.class));
95+
96+
subscription.unsubscribe();
97+
98+
verify(handler).removeCallbacks(Matchers.any(Runnable.class));
99+
}
100+
101+
@Test
102+
public void shouldNotCallOnSubscribeWhenSubscriptionUnsubscribedBeforeDelay() {
103+
final Observable.OnSubscribe<Integer> onSubscribe = mock(Observable.OnSubscribe.class);
104+
final Handler handler = spy(new Handler());
105+
106+
final Scheduler scheduler = new HandlerThreadScheduler(handler);
107+
final Worker worker = spy(scheduler.createWorker());
108+
109+
final Scheduler spyScheduler = spy(scheduler);
110+
when(spyScheduler.createWorker()).thenReturn(worker);
111+
112+
final Subscription subscription = Observable.create(onSubscribe)
113+
.delaySubscription(1, TimeUnit.MINUTES, spyScheduler)
114+
.subscribe();
115+
116+
verify(worker).schedule(Matchers.any(Action0.class),
117+
Matchers.eq(1L), Matchers.eq(TimeUnit.MINUTES));
118+
verify(handler).postDelayed(Matchers.any(Runnable.class),
119+
Matchers.eq(TimeUnit.MINUTES.toMillis(1L)));
120+
121+
subscription.unsubscribe();
122+
123+
Robolectric.runUiThreadTasksIncludingDelayedTasks();
124+
125+
verify(onSubscribe, never()).call(Matchers.any(Subscriber.class));
126+
verify(handler).removeCallbacks(Matchers.any(Runnable.class));
127+
}
78128
}

0 commit comments

Comments
 (0)