Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern trends of Android approaches. Our way in R.I.D.

Modern trends of Android approaches. Our way in R.I.D.

Sergiy Grechukha – Android engineer, Techery
Working in a startUp is some kind of a challenge – one never knows what`s behind the corner. A lot of things depend on the basic architecture decisions – scalability, testability, flexibility and many other abilities. This talk covers our way of using androids modern architectural approaches, explains why do we use Dagger2, how we figured layers of app, what kind of MV pattern we use and why. Also we`ll dive in testing (unit and UI), discuss how to measure efficiency of tests and their goals. As bonus some apt tricks (if we`ll have a time for this stuff.

GDG Cherkasy

February 24, 2017
Tweet

More Decks by GDG Cherkasy

Other Decks in Programming

Transcript

  1. Architectural decisions UI Presenter Repository Local storage Remote storage 1.

    Retrofit 2. Crashlytics 3. Gson 1. Gson 2. SharedPrefs 3. Realm(in plans) 1. rxjava 1. Rxjava 2. Glide 3. Support (Constrain layout) 4. DataBinding 5. Facebook sdk 6. Firebase 7. VK sdk 1. Retrolambda 2. Dagger 3. annimon:stream 4. Crashlytics 5. Own libs
  2. Dagger2 structure in R.I.D. AppComponent: - Repository - Context Activity1:

    Inject in Activity1 class Repository Activity2: Inject in Activity1 class Repository ActivityN: Inject in Activity1 class Repository (first edition) Not so good ☹
  3. Dagger2 structure in R.I.D. AppComponent: - Repository - Context -

    ModuleFactory BaseActivityComponent: - BaseActivity Presenters (Inject in BaseActivity class) ActivityComponent_1: - Activity_1 Presenters (Inject in Activity1 class) ActivityComponent_2: - Activity_2 Presenters (Inject in Activity2 class) ActivityComponent_N: - Activity_1 Presenters (Inject in ActivityN class) Any other app component (second edition)
  4. Dagger2 structure in R.I.D. (code samples) AppComponent AppModule @Module public

    class AppModule { @Singleton @Provides public Context provideContext() {return mContext;} @Singleton @Provides public Repository provideRepository() { return getRepository();} @NonNull public Repository getRepository() { return new Repository(new SharedPrefsStorage(…), new ServerHelper(…));} @Provides @Singleton public PresenterFactoryExtended provideExtendedModuleFactory() { return getExtendedModuleFactory();} @Singleton @Component(modules = AppModule.class) public interface AppComponent { void inject(IntroActivity activity); void inject(SplashActivity activity); PresenterFactoryExtended getExtendedPresenterFactory(); BaseActivityComponent plus(BaseActivityModule module); }
  5. Dagger2 structure in R.I.D. (code samples) BaseActivityComponent BaseActivityModule @Module @Singleton

    public class BaseActivityModule { @Provides public SuggestWordPresenter provideSuggestPresenter(Repository repository) { return getSuggestPresenter(repository); } @Provides @Named("BaseActivity") public UserAccountPresenter provideUserPresenter(Repository repository) { return getUserPresenter(repository); } @BaseActivityScope @Subcomponent(modules = BaseActivityModule.class) @Singleton public interface BaseActivityComponent { void inject(BaseActivity activity); PresenterFactory getScopeModule(); FirstComponent plus(FirstModule module); … NthComponent plus(NthModule module); }
  6. Dagger2 structure in R.I.D. (code samples) ActivityComponent ActivityModule package com.smartpresenter.generated;

    @Module public class DailyWordsModule { private com.ua.rid.contract.DailyWordsContract.View mContract; public DailyWordsModule(com.ua.rid.contract.DailyWordsContract.View contract) { this.mContract = contract; } @Provides public com.ua.rid.presenter.DailyWordsPresenter providePresenter(com.ua.rid.model.Repository repository){ return getPresenter(repository); } package com.smartpresenter.generated; import dagger.Subcomponent; import com.ua.rid.di.ActivityScope; @ActivityScope @Subcomponent(modules = com.smartpresenter.generated.DailyWordsModule.class) public interface DailyWordsComponent { void inject(com.ua.rid.ui.activity.DailyWordsActivity target); }
  7. Dagger2 structure in R.I.D. (code samples) In BaseActivity class That

    is all: just use presenter now In Activity class P.S. Have you noticed generated classes? public BaseActivityComponent getBaseActivityComponent() { if (baseactivityComponent == null) { AppComponent comp = ((RidApp) getApplication()).getAppComponent(); baseactivityComponent = comp.plus(comp.getExtendedPresenterFactory() .provideBaseActivityModule(this, this)); } return baseactivityComponent; } @Inject DictionaryPresenter mPresenter; private void injectDependencies() { mDictionaryComponent = getAppComponent().plus(getBaseActivityComponent().getScopeModule().getModule(this)); mDictionaryComponent.inject(this); }
  8. Test advocating 1. Be sure in your code validity 2.

    Makes your code clean 3. Keep calm and go refactor 4. Describes your dev level 5. World wide trend!!!
  9. UI Tests Tests hook for dagger @Module public class TestAppModule

    extends AppModule { @Override public PresenterFactory getModuleFactory() { return Mockito.mock(PresenterFactory.class); } @Override protected PresenterFactoryExtended getExtendedModuleFactory() { return Mockito.mock(PresenterFactoryExtended.class); } @NonNull @Override public Repository getRepository() { return Mockito.mock(Repository.class); } } 1. Override AppModule and return mocks
  10. UI Tests Tests hook for dagger 2. Create custom test

    rule public class MyTestRule implements TestRule { public MyTestRule(Context context) { … mTestComponent = DaggerTestAppComponent .builder() .testAppModule(new TestAppModule(application.getApplicationContext())).build(); } @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { … application.setMainComponent(mTestComponent); base.evaluate(); application.setMainComponent(null); } }; } }
  11. UI Tests Tests hook for dagger 3. Chain rules in

    test final MyTestRule component = new MyTestRule(InstrumentationRegistry.getTargetContext()); private final ActivityTestRule<DailyWordsActivity> mActivityRule = new ActivityTestRule<>(YourActivity.class, false, false); @Rule public TestRule chain = RuleChain.outerRule(component).around(mActivityRule); @Override public void setUp() { super.setUp(); setUpPresenter(); } private void setUpPresenter() { when(mMockPresenterFactory.getModule(mCallBackCaptor.capture())).thenReturn(mMockModule); when(mMockModule.providePresenter(any())).thenReturn(mMockPresenter); } 4. SetUp your mocks
  12. UI Tests Simplicity of Espresso onView(withId(R.id.tv_mainWord1)).check(matches(withText("test1"))); Check if given view

    shows needed text onView(withId(R.id.dictionary_recycler_view)).check(new RecyclerViewItemCountAssertion(6)); Check if given RecyclerView has 6 childs if (isElementVisible(withId(R.id.dictionary_recycler_view))) { onView(withId(R.id.dictionary_recycler_view)).perform(RecyclerViewActions .actionOnItemAtPosition(1, click())); //check intent intended(expectedIntent); Intents.release(); } else { assertTrue("Failed to find recycler", false); } Click RecyclerView child at position == 1 and check intent Espresso cheat sheet
  13. UI Tests What exactly is tested? 1. View logic (what

    will happen if one clicks this button?) 2. Do not test presenters MOCK them 3. Be aware of animations (of any kind) 4. Do not test changes of actvities, test intents
  14. Unit tests Again what exactly is tested? 1. Test YOUR

    logic (2 + 2 = 4?) 2. Test edge cases 3. Avoid testing android 4. Do not test 3d party libs (smbd`s already done) 5. Try to test all public methods 6. I`m pretty sure U must not test private methods… (if your code is good it`s done automatically)
  15. Unit tests RX Schedulers io() is waiting for smth but

    test won`t. Custom Scheduler public class AppSchedulers { public interface SchedulerProvider { Scheduler mainThread(); Scheduler io(); } public static Scheduler mainThread() { return instance.mainThread(); } public static Scheduler io() { return instance.io(); } public static class DefaultSchedulerProvider implements SchedulerProvider { @Override public Scheduler mainThread() { return AndroidSchedulers.mainThread(); } @Override public Scheduler io() { return Schedulers.io(); } } }
  16. Unit tests RX Schedulers Custom rule -> substitute real Scheduler

    public class SynchronousSchedulers extends ExternalResource { @Override protected void before() throws Throwable { AppSchedulers.setInstance(new AppSchedulers.SchedulerProvider() { @Override public Scheduler mainThread() { return Schedulers.immediate(); } @Override public Scheduler io() { return Schedulers.immediate(); } }); } @Override protected void after() { AppSchedulers.setInstance(new AppSchedulers.DefaultSchedulerProvider()); } } Yes, I know about TestSubscriber, but it`s our LSD, our universe, our Schedulers))))
  17. Reading test results 1. UI + unit results 2. Lines

    coverage 3. Branches cov. 4. Static code analysis few words How to merge coverage*
  18. Plans 1. Continuous integration 2. Apps environment infrastructure (more apps)

    3. Project infrastructure (marketing, offline events)
  19. We are hiring 1. IOS developer 2. UI/UX designer 3.

    Front end developer Find us: http://rid.ck.ua/