building maintainable app

70
Building maintainable app with MVP and Dagger2 KRISTIJ AN JURKOVIĆ ANDROID TEAM LEAD @ INFINUM

Upload: kristijan-jurkovic

Post on 10-Jan-2017

1.134 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Building maintainable app

Building maintainable app with MVP and Dagger2KRISTIJAN JURKOVIĆ ANDROID TEAM LEAD @ INFINUM

Page 2: Building maintainable app

We're an independent design & development agency.

Page 3: Building maintainable app
Page 4: Building maintainable app

INFINUM

• 90 people in 3 offices

• 15 android developers

• hundreds of projects

Page 5: Building maintainable app

OUR BIGGEST ISSUES?

Page 6: Building maintainable app

“Sometimes when you fill a vacuum, it still sucks.”

― Dennis Ritchie

Page 7: Building maintainable app

MVP TO THE RESCUE?

Page 8: Building maintainable app

PROGRAM TO INTERFACES NOT IMPLEMENTATIONS

Page 9: Building maintainable app

ModelPresenterView

Page 10: Building maintainable app

LoginActivity LoginPresenterImpl LoginInteractorImpl

LoginView LoginPresenter LoginInteractor

showLoading() hideLoading() setUsernameError() setPasswordError()

showLoading() hideLoading() setUsernameError() setPasswordError()

login(username, pass)

loginPresenter loginView loginInteractor

login(username, pass) login(username, pass, listener)

login(username, pass, listener)

VIEW PRESENTER MODEL

Page 11: Building maintainable app

public interface HomeView { ...}

public interface HomePresenter { ...}

public interface CurrencyInteractor { ...}

VIEW

PRESENTER

MODEL

Page 12: Building maintainable app

public interface HomeView { ...}

VIEW

Page 13: Building maintainable app

public class HomeActivity extends BaseActivity implements HomeView {

// this is an interface HomePresenter presenter;

...}

Page 14: Building maintainable app

public interface HomePresenter { ...}

PRESENTER

Page 15: Building maintainable app

public class HomePresenterImpl implements HomePresenter {

// interface private HomeView view;

// and another interface private CurrencyInteractor interactor;

public HomePresenterImpl(HomeView view, CurrencyInteractor interactor) {

this.view = view; this.interactor = interactor; }

... }

Page 16: Building maintainable app

public interface CurrencyInteractor { ...}

MODEL

Page 17: Building maintainable app

public class CurrencyInteractorImpl implements CurrencyInteractor {

... }

Page 18: Building maintainable app

HOW SHOULD I GET MY CONTENT?

Page 19: Building maintainable app

public interface HomeView {void showCurrencies(List<Currency> currencies);

}

public interface HomePresenter { void loadCurrencyList();}

public interface CurrencyInteractor { void getCurrencyList(CurrencyListener listener);}

Page 20: Building maintainable app

public class HomeActivity extends BaseActivity implements HomeView {

private void init() { presenter = new HomePresenterImpl(this,

new CurrencyInteractorImpl()); presenter.getCurrencyList(); }

@Override public void showCurrencies(List<Currency> currencies) { // display data }

}

Page 21: Building maintainable app

public class HomePresenterImpl implements HomePresenter {

...@Override public void loadCurrencyList() { interactor.getCurrencyList(...); }

}

Page 22: Building maintainable app

public class CurrencyInteractorImpl implements CurrencyInteractor {

... @Override public void getCurrencyList(

CurrencyListener listener) {

// do API/DB call // return result with listener }

}

Page 23: Building maintainable app

VIEW SHOULDN’T CREATE ITS DEPENDENCIES

Page 24: Building maintainable app

DEPENDENCY INJECTION

Page 25: Building maintainable app

JSR 330

• 5 annotations - @Named, @Inject, @Qualifier, @Scope,

@Singleton

• 1 interface - Provider<T>

Page 26: Building maintainable app

DAGGER2 TO THE RESCUE

Page 27: Building maintainable app

DAGGER 2

• @Module, @Provides, @Component, @Subcomponent,

ScopedProvider

• Injection into Fields, Constructors, Methods

• Each @Inject has to have its @Provides

Page 28: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

Page 29: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

Page 30: Building maintainable app

@Modulepublic class ApiModule {

@Provides @Singleton public ApiService provideApiService(

OkHttpClient client, BaseUrl endpoint, Converter.Factory converter) {

return RestUtils.createApiService(

client, endpoint, converter, ApiService.class);

}}

Page 31: Building maintainable app

@Modulepublic class ApiModule {

@Provides @Singleton public ApiService provideApiService(

OkHttpClient client, BaseUrl endpoint, Converter.Factory converter) {

return RestUtils.createApiService(

client, endpoint, converter, ApiService.class);

}}

Page 32: Building maintainable app

@Modulepublic class ApiModule {

@Provides @Singleton public ApiService provideApiService(

OkHttpClient client, BaseUrl endpoint, Converter.Factory converter) {

return RestUtils.createApiService(

client, endpoint, converter, ApiService.class);

}}

Page 33: Building maintainable app

@Modulepublic class GsonConverterModule {

@Provides @Singleton public Converter.Factory

provideConverter(Gson gson) {

return GsonConverterFactory.create(gson); }}

Page 34: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

EXECUTORS MODULE

Page 35: Building maintainable app

@Component(modules = { HostModule.class, GsonConverterModule.class, ClientModule.class, LoggerModule.class, ExecutorsModule.class, ApiModule.class, GsonModule.class})@Singletonpublic interface AppComponent {}

Page 36: Building maintainable app

public class MyApplication extends Application {

protected AppComponent appComponent;

protected void init() { appComponent = DaggerAppComponent.create(); }}

Page 37: Building maintainable app

HOW CAN WE REUSE THAT IN OUR ACTIVITIES?

Page 38: Building maintainable app

public class HomeActivity extends BaseActivity implements HomeView {

private void init() { presenter = new HomePresenterImpl(this,

new CurrencyInteractorImpl()); presenter.getCurrencyList(); }

@Override public void showCurrencies(List<Currency> currencies) { // display data }

}

Page 39: Building maintainable app

• Inject presenter into view

• Inject view and interactor into presenter

Page 40: Building maintainable app

@Modulepublic class HomeModule {

private HomeView view; public HomeModule(HomeView view) { this.view = view; }

@Provides public HomeView provideView() { return view; }

@Provides public HomePresenter providePresenter(HomePresenterImpl presenter) { return presenter; }

@Provides public CurrencyInteractor provideInteractor(

CurrencyInteractorImpl interactor) { return interactor; }}

Page 41: Building maintainable app

@Modulepublic class HomeModule {

private HomeView view; public HomeModule(HomeView view) { this.view = view; }

@Provides public HomeView provideView() { return view; }

@Provides public HomePresenter providePresenter(HomePresenterImpl presenter) { return presenter; }

@Provides public CurrencyInteractor provideInteractor(

CurrencyInteractorImpl interactor) { return interactor; }}

Page 42: Building maintainable app

@Modulepublic class HomeModule {

private HomeView view; public HomeModule(HomeView view) { this.view = view; }

@Provides public HomeView provideView() { return view; }

@Provides public HomePresenter providePresenter(HomePresenterImpl presenter) { return presenter; }

@Provides public CurrencyInteractor provideInteractor(

CurrencyInteractorImpl interactor) { return interactor; }}

Page 43: Building maintainable app

public class CurrencyInteractorImpl implements CurrencyInteractor {

@Injectpublic CurrencyInteractorImpl(ApiService service) {

}}

Page 44: Building maintainable app

public class CurrencyInteractorImpl implements CurrencyInteractor {

@Injectpublic CurrencyInteractorImpl(ApiService service) {

}}

Page 45: Building maintainable app

@Modulepublic class HomeModule {

private HomeView view; public HomeModule(HomeView view) { this.view = view; }

@Provides public HomeView provideView() { return view; }

@Provides public HomePresenter providePresenter(HomePresenterImpl presenter) { return presenter; }

@Provides public CurrencyInteractor provideInteractor(

CurrencyInteractorImpl interactor) { return interactor; }}

Page 46: Building maintainable app

public class HomePresenterImpl implements HomePresenter {

@Inject public HomePresenterImpl(HomeView view,

CurrencyInteractor interactor) {

this.view = view; this.interactor = interactor; }}

Page 47: Building maintainable app

public class HomeActivity extends BaseActivity implements HomeView {

@Inject HomePresenter presenter;

}

Page 48: Building maintainable app

@Subcomponent(modules = HomeModule.class)public interface HomeComponent { void inject(HomeActivity activity);}

Page 49: Building maintainable app

WHAT’S THAT “SUBCOMPONENT” THING

YOU MENTIONED?

Page 50: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

HOME MODULE

HOMECOMPONENT

EXECUTORS MODULE

Page 51: Building maintainable app

@Component(modules = { ...})@Singletonpublic interface AppComponent {

HomeComponent plus(HomeModule module);}

Page 52: Building maintainable app

public abstract class BaseActivity extends AppCompatActivity {

Override protected void onCreate(Bundle savedInstanceState) { ... injectDependencies(MyApplication.getAppComponent()); }

protected abstract void injectDependencies(AppComponent appComponent);

}

Page 53: Building maintainable app

public class HomeActivity extends BaseActivity implements HomeView {

protected void injectDependencies(AppComponent appComponent) {

appComponent.plus(new HomeModule(this)).inject(this);

}}

Page 54: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

HOME MODULE

HOMECOMPONENTSESSIONCOMPONENT

SESSION MODULE

EXECUTORS MODULE

Page 55: Building maintainable app

SATISFACTION LEVEL 9001

Page 56: Building maintainable app

“If you don’t like testing your product, most likely your customers won’t like to test it

either.”

Page 57: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

EXECUTORS MODULE

Page 58: Building maintainable app

APP COMPONENT

HOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

EXECUTORS MODULE

Page 59: Building maintainable app

APPTESTCOMPONENT

MOCKHOST MODULE

CONVERTER MODULE

CLIENT MODULE

LOGGER MODULE

API MODULE

GSON MODULE

SYNC EXECUTORS

MODULE

Page 60: Building maintainable app

@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {

void inject(MyTestApplication app);}

Page 61: Building maintainable app

@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {

void inject(MyTestApplication app);}

Page 62: Building maintainable app

@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {

void inject(MyTestApplication app);}

Page 63: Building maintainable app

public class MyTestApplication extends MyApplication implements TestLifecycleApplication {

@Override protected void init() { appComponent = DaggerAppTestComponent.create(); }}

Page 64: Building maintainable app

protected void enqueueResponse(String filename) { String body = ResourceUtils.readFromFile(filename); MockResponse mockResponse =

new MockResponse().setBody(body).setResponseCode(HttpURLConnection.HTTP_OK);

mockWebServer.enqueue(mockResponse);}

Page 65: Building maintainable app

@Overridepublic void setup() throws Exception { super.setup(); controller = Robolectric

.buildActivity(MockActivity.class)

.create()

.start() .resume() .visible();

fragment = DashboardDrivingModeFragment.newInstance();

controller.get().getSupportFragmentManager() .beginTransaction() .replace(R.id.container, fragment, null) .commit(); ButterKnife.bind(this, fragment.getView());}

Page 66: Building maintainable app

@Testpublic void testEmptyStateNotVisible() { enqueueResponse(“rest-currency-response.json”); btnCurrencyList.performClick(); assertThat(emptyView).isNotVisible();}

Page 67: Building maintainable app

THINGS TO REMEMBER

• Orientation change

Page 68: Building maintainable app

THINGS TO REMEMBER

• Dagger2 is a powerful tool - make good use of it

• Save yourselves from regression bugs

Page 69: Building maintainable app

REFERENCES

• http://antonioleiva.com/mvp-android/

• https://medium.com/@czyrux/presenter-surviving-

orientation-changes-with-

loaders-6da6d86ffbbf#.xou7c71uz

• http://frogermcs.github.io/dependency-injection-with-

dagger-2-custom-scopes/

• https://www.youtube.com/watch?v=oK_XtfXPkqw

Page 70: Building maintainable app

Any questions? KRISTIJ[email protected] @KJURKOVIC

Visit infinum.co or find us on social networks:

infinum.co infinumco infinumco infinum