Skip to content

Commit dc22e6c

Browse files
author
Ivan Carballo
committed
Remove SchedulerAppliers and use Espresso idling resouces for handling RxJava Observable executions
1 parent f64f493 commit dc22e6c

File tree

18 files changed

+134
-238
lines changed

18 files changed

+134
-238
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ android {
1919
applicationId 'uk.co.ribot.androidboilerplate'
2020
minSdkVersion 16
2121
targetSdkVersion 23
22-
testInstrumentationRunner 'uk.co.ribot.androidboilerplate.util.UnlockDeviceTestRunner'
22+
testInstrumentationRunner "${applicationId}.runner.RxAndroidJUnitRunner"
2323
versionCode 1000
2424
// Major -> Millions, Minor -> Thousands, Bugfix -> Hundreds. E.g 1.3.72 == 1,003,072
2525
versionName '0.1.0'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package uk.co.ribot.androidboilerplate.runner;
2+
3+
import android.os.Bundle;
4+
import android.support.test.espresso.Espresso;
5+
6+
import rx.plugins.RxJavaPlugins;
7+
import uk.co.ribot.androidboilerplate.util.RxIdlingExecutionHook;
8+
import uk.co.ribot.androidboilerplate.util.RxIdlingResource;
9+
10+
/**
11+
* Runner that registers a Espresso Indling resource that handles waiting for
12+
* RxJava Observables to finish.
13+
* WARNING - Using this runner will block the tests if the application uses long-lived hot
14+
* Observables such us event buses, etc.
15+
*/
16+
public class RxAndroidJUnitRunner extends UnlockDeviceAndroidJUnitRunner {
17+
18+
@Override
19+
public void onCreate(Bundle arguments) {
20+
super.onCreate(arguments);
21+
RxIdlingResource rxIdlingResource = new RxIdlingResource();
22+
RxJavaPlugins.getInstance()
23+
.registerObservableExecutionHook(new RxIdlingExecutionHook(rxIdlingResource));
24+
Espresso.registerIdlingResources(rxIdlingResource);
25+
}
26+
}

app/src/androidTest/java/uk/co/ribot/androidboilerplate/util/UnlockDeviceTestRunner.java renamed to app/src/androidTest/java/uk/co/ribot/androidboilerplate/runner/UnlockDeviceAndroidJUnitRunner.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package uk.co.ribot.androidboilerplate.util;
1+
package uk.co.ribot.androidboilerplate.runner;
22

33
import android.app.Application;
44
import android.app.KeyguardManager;
@@ -11,14 +11,18 @@
1111
import static android.os.PowerManager.FULL_WAKE_LOCK;
1212
import static android.os.PowerManager.ON_AFTER_RELEASE;
1313

14-
public class UnlockDeviceTestRunner extends AndroidJUnitRunner {
14+
/**
15+
* Extension of AndroidJUnitRunner that adds some functionality to unblock the device screen
16+
* before starting the tests.
17+
*/
18+
public class UnlockDeviceAndroidJUnitRunner extends AndroidJUnitRunner {
1519

1620
private PowerManager.WakeLock mWakeLock;
1721

1822
@Override
1923
public void onStart() {
2024
Application application = (Application) getTargetContext().getApplicationContext();
21-
String simpleName = UnlockDeviceTestRunner.class.getSimpleName();
25+
String simpleName = UnlockDeviceAndroidJUnitRunner.class.getSimpleName();
2226
// Unlock the device so that the tests can input keystrokes.
2327
((KeyguardManager) application.getSystemService(KEYGUARD_SERVICE))
2428
.newKeyguardLock(simpleName)
@@ -36,4 +40,4 @@ public void onDestroy() {
3640
super.onDestroy();
3741
mWakeLock.release();
3842
}
39-
}
43+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package uk.co.ribot.androidboilerplate.util;
2+
3+
import rx.Observable;
4+
import rx.Subscription;
5+
import rx.plugins.RxJavaObservableExecutionHook;
6+
7+
/**
8+
* RxJava Observable execution hook that handles updating the active subscription
9+
* count for a given Espresso RxIdlingResource.
10+
*/
11+
public class RxIdlingExecutionHook extends RxJavaObservableExecutionHook {
12+
13+
private RxIdlingResource mRxIdlingResource;
14+
15+
public RxIdlingExecutionHook(RxIdlingResource rxIdlingResource) {
16+
mRxIdlingResource = rxIdlingResource;
17+
}
18+
19+
@Override
20+
public <T> Observable.OnSubscribe<T> onSubscribeStart(
21+
Observable<? extends T> observableInstance, Observable.OnSubscribe<T> onSubscribe) {
22+
mRxIdlingResource.incrementActiveSubscriptionsCount();
23+
return super.onSubscribeStart(observableInstance, onSubscribe);
24+
}
25+
26+
@Override
27+
public <T> Throwable onSubscribeError(Throwable e) {
28+
mRxIdlingResource.decrementActiveSubscriptionsCount();
29+
return super.onSubscribeError(e);
30+
}
31+
32+
@Override
33+
public <T> Subscription onSubscribeReturn(Subscription subscription) {
34+
mRxIdlingResource.decrementActiveSubscriptionsCount();
35+
return super.onSubscribeReturn(subscription);
36+
}
37+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package uk.co.ribot.androidboilerplate.util;
2+
3+
import android.support.test.espresso.IdlingResource;
4+
5+
import java.util.concurrent.atomic.AtomicInteger;
6+
7+
import timber.log.Timber;
8+
9+
/**
10+
* Espresso Idling resource that handles waiting for RxJava Observables executions.
11+
* This class must be used with RxIdlingExecutionHook.
12+
* Before registering this idling resource you must:
13+
* 1. Create an instance of RxIdlingExecutionHook by passing an instance of this class.
14+
* 2. Register RxIdlingExecutionHook with the RxJavaPlugins using registerObservableExecutionHook()
15+
* 3. Register this idle resource with Espresso using Espresso.registerIdlingResources()
16+
*/
17+
public class RxIdlingResource implements IdlingResource {
18+
19+
private final AtomicInteger mActiveSubscriptionsCount = new AtomicInteger(0);
20+
private ResourceCallback mResourceCallback;
21+
22+
@Override
23+
public String getName() {
24+
return getClass().getSimpleName();
25+
}
26+
27+
@Override
28+
public boolean isIdleNow() {
29+
return mActiveSubscriptionsCount.get() == 0;
30+
}
31+
32+
@Override
33+
public void registerIdleTransitionCallback(ResourceCallback callback) {
34+
mResourceCallback = callback;
35+
}
36+
37+
public void incrementActiveSubscriptionsCount() {
38+
int count = mActiveSubscriptionsCount.incrementAndGet();
39+
Timber.i("Active subscriptions count increased to %d", count);
40+
}
41+
42+
public void decrementActiveSubscriptionsCount() {
43+
int count = mActiveSubscriptionsCount.decrementAndGet();
44+
Timber.i("Active subscriptions count decreased to %d", count);
45+
if (isIdleNow()) {
46+
Timber.i("There is no active subscriptions, transitioning to Idle");
47+
mResourceCallback.onTransitionToIdle();
48+
}
49+
}
50+
51+
}

app/src/commonTest/java/uk/co/ribot/androidboilerplate/test/common/injection/component/TestComponent.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
import dagger.Component;
66
import uk.co.ribot.androidboilerplate.injection.component.ApplicationComponent;
77
import uk.co.ribot.androidboilerplate.test.common.injection.module.ApplicationTestModule;
8-
import uk.co.ribot.androidboilerplate.test.common.injection.module.DefaultSchedulersTestModule;
98

109
@Singleton
11-
@Component(modules = {ApplicationTestModule.class, DefaultSchedulersTestModule.class})
10+
@Component(modules = ApplicationTestModule.class)
1211
public interface TestComponent extends ApplicationComponent {
1312

1413
}

app/src/commonTest/java/uk/co/ribot/androidboilerplate/test/common/injection/module/ApplicationTestModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Application provideApplication() {
3838
@Singleton
3939
DataManager provideDataManager() {
4040
TestDataManager testDataManager = new TestDataManager(mApplication);
41-
return mMockableDataManager ? mock(DataManager.class) : testDataManager;
41+
return mMockableDataManager ? spy(testDataManager) : testDataManager;
4242
}
4343

4444
@Provides

app/src/commonTest/java/uk/co/ribot/androidboilerplate/test/common/injection/module/DefaultSchedulersTestModule.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

app/src/main/java/uk/co/ribot/androidboilerplate/data/SyncService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111

1212
import rx.Observer;
1313
import rx.Subscription;
14+
import rx.schedulers.Schedulers;
1415
import timber.log.Timber;
1516
import uk.co.ribot.androidboilerplate.BoilerplateApplication;
1617
import uk.co.ribot.androidboilerplate.data.model.Ribot;
1718
import uk.co.ribot.androidboilerplate.util.AndroidComponentUtil;
1819
import uk.co.ribot.androidboilerplate.util.NetworkUtil;
19-
import uk.co.ribot.androidboilerplate.util.SchedulerAppliers;
2020

2121
public class SyncService extends Service {
2222

@@ -50,7 +50,7 @@ public int onStartCommand(Intent intent, int flags, final int startId) {
5050

5151
if (mSubscription != null && !mSubscription.isUnsubscribed()) mSubscription.unsubscribe();
5252
mSubscription = mDataManager.syncRibots()
53-
.compose(SchedulerAppliers.<Ribot>defaultSubscribeScheduler())
53+
.subscribeOn(Schedulers.io())
5454
.subscribe(new Observer<Ribot>() {
5555
@Override
5656
public void onCompleted() {

app/src/main/java/uk/co/ribot/androidboilerplate/injection/component/ApplicationComponent.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010
import uk.co.ribot.androidboilerplate.data.DataManager;
1111
import uk.co.ribot.androidboilerplate.data.SyncService;
1212
import uk.co.ribot.androidboilerplate.injection.module.ApplicationModule;
13-
import uk.co.ribot.androidboilerplate.injection.module.DefaultSchedulersModule;
1413
import uk.co.ribot.androidboilerplate.ui.main.MainPresenter;
15-
import uk.co.ribot.androidboilerplate.util.SchedulerApplier;
1614

1715
@Singleton
18-
@Component(modules = {ApplicationModule.class, DefaultSchedulersModule.class})
16+
@Component(modules = ApplicationModule.class)
1917
public interface ApplicationComponent {
2018

2119
void inject(SyncService syncService);

app/src/main/java/uk/co/ribot/androidboilerplate/injection/component/DefaultSchedulersComponent.java

Lines changed: 0 additions & 12 deletions
This file was deleted.

app/src/main/java/uk/co/ribot/androidboilerplate/injection/module/DefaultSchedulersModule.java

Lines changed: 0 additions & 33 deletions
This file was deleted.

app/src/main/java/uk/co/ribot/androidboilerplate/ui/base/BaseActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ protected void onCreate(Bundle savedInstanceState) {
2020
public ActivityComponent getActivityComponent() {
2121
if (mActivityComponent == null) {
2222
mActivityComponent = DaggerActivityComponent.builder()
23+
.presentersModule(new PresentersModule(getApplication()))
2324
.applicationComponent(BoilerplateApplication.get(this).getComponent())
2425
.build();
2526
}

app/src/main/java/uk/co/ribot/androidboilerplate/ui/main/MainActivity.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ public void showRibots(List<Ribot> ribots) {
4848
}
4949

5050
@Override
51-
public void showError(String errorMessage) {
52-
DialogFactory.createGenericErrorDialog(this, errorMessage).show();
51+
public void showError() {
52+
DialogFactory.createGenericErrorDialog(this, getString(R.string.error_loading_ribots))
53+
.show();
5354
}
5455

5556
@Override

app/src/main/java/uk/co/ribot/androidboilerplate/ui/main/MainMvpView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public interface MainMvpView extends MvpView {
1111

1212
void showRibotsEmpty();
1313

14-
void showError(String errorMessage);
14+
void showError();
1515

1616
}

app/src/main/java/uk/co/ribot/androidboilerplate/ui/main/MainPresenter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ public void onCompleted() {
4949
@Override
5050
public void onError(Throwable e) {
5151
Timber.e(e, "There was an error loading the ribots.");
52-
//todo String errorString = getContext().getString(R.string.error_loading_ribots);
53-
getMvpView().showError("todo change");
52+
getMvpView().showError();
5453
}
5554

5655
@Override

0 commit comments

Comments
 (0)