Download - Building maintainable app
![Page 1: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/1.jpg)
Building maintainable app with MVP and Dagger2KRISTIJAN JURKOVIĆ ANDROID TEAM LEAD @ INFINUM
![Page 2: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/2.jpg)
We're an independent design & development agency.
![Page 3: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/3.jpg)
![Page 4: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/4.jpg)
INFINUM
• 90 people in 3 offices
• 15 android developers
• hundreds of projects
![Page 5: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/5.jpg)
OUR BIGGEST ISSUES?
![Page 6: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/6.jpg)
“Sometimes when you fill a vacuum, it still sucks.”
― Dennis Ritchie
![Page 7: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/7.jpg)
MVP TO THE RESCUE?
![Page 8: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/8.jpg)
PROGRAM TO INTERFACES NOT IMPLEMENTATIONS
![Page 9: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/9.jpg)
ModelPresenterView
![Page 10: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/10.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/11.jpg)
public interface HomeView { ...}
public interface HomePresenter { ...}
public interface CurrencyInteractor { ...}
VIEW
PRESENTER
MODEL
![Page 12: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/12.jpg)
public interface HomeView { ...}
VIEW
![Page 13: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/13.jpg)
public class HomeActivity extends BaseActivity implements HomeView {
// this is an interface HomePresenter presenter;
...}
![Page 14: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/14.jpg)
public interface HomePresenter { ...}
PRESENTER
![Page 15: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/15.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/16.jpg)
public interface CurrencyInteractor { ...}
MODEL
![Page 17: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/17.jpg)
public class CurrencyInteractorImpl implements CurrencyInteractor {
... }
![Page 18: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/18.jpg)
HOW SHOULD I GET MY CONTENT?
![Page 19: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/19.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/20.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/21.jpg)
public class HomePresenterImpl implements HomePresenter {
...@Override public void loadCurrencyList() { interactor.getCurrencyList(...); }
}
![Page 22: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/22.jpg)
public class CurrencyInteractorImpl implements CurrencyInteractor {
... @Override public void getCurrencyList(
CurrencyListener listener) {
// do API/DB call // return result with listener }
}
![Page 23: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/23.jpg)
VIEW SHOULDN’T CREATE ITS DEPENDENCIES
![Page 24: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/24.jpg)
DEPENDENCY INJECTION
![Page 25: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/25.jpg)
JSR 330
• 5 annotations - @Named, @Inject, @Qualifier, @Scope,
@Singleton
• 1 interface - Provider<T>
![Page 26: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/26.jpg)
DAGGER2 TO THE RESCUE
![Page 27: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/27.jpg)
DAGGER 2
• @Module, @Provides, @Component, @Subcomponent,
ScopedProvider
• Injection into Fields, Constructors, Methods
• Each @Inject has to have its @Provides
![Page 28: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/28.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
![Page 29: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/29.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
![Page 30: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/30.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/31.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/32.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/33.jpg)
@Modulepublic class GsonConverterModule {
@Provides @Singleton public Converter.Factory
provideConverter(Gson gson) {
return GsonConverterFactory.create(gson); }}
![Page 34: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/34.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
EXECUTORS MODULE
![Page 35: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/35.jpg)
@Component(modules = { HostModule.class, GsonConverterModule.class, ClientModule.class, LoggerModule.class, ExecutorsModule.class, ApiModule.class, GsonModule.class})@Singletonpublic interface AppComponent {}
![Page 36: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/36.jpg)
public class MyApplication extends Application {
protected AppComponent appComponent;
protected void init() { appComponent = DaggerAppComponent.create(); }}
![Page 37: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/37.jpg)
HOW CAN WE REUSE THAT IN OUR ACTIVITIES?
![Page 38: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/38.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/39.jpg)
• Inject presenter into view
• Inject view and interactor into presenter
![Page 40: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/40.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/41.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/42.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/43.jpg)
public class CurrencyInteractorImpl implements CurrencyInteractor {
@Injectpublic CurrencyInteractorImpl(ApiService service) {
}}
![Page 44: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/44.jpg)
public class CurrencyInteractorImpl implements CurrencyInteractor {
@Injectpublic CurrencyInteractorImpl(ApiService service) {
}}
![Page 45: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/45.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/46.jpg)
public class HomePresenterImpl implements HomePresenter {
@Inject public HomePresenterImpl(HomeView view,
CurrencyInteractor interactor) {
this.view = view; this.interactor = interactor; }}
![Page 47: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/47.jpg)
public class HomeActivity extends BaseActivity implements HomeView {
@Inject HomePresenter presenter;
}
![Page 48: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/48.jpg)
@Subcomponent(modules = HomeModule.class)public interface HomeComponent { void inject(HomeActivity activity);}
![Page 49: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/49.jpg)
WHAT’S THAT “SUBCOMPONENT” THING
YOU MENTIONED?
![Page 50: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/50.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
HOME MODULE
HOMECOMPONENT
EXECUTORS MODULE
![Page 51: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/51.jpg)
@Component(modules = { ...})@Singletonpublic interface AppComponent {
HomeComponent plus(HomeModule module);}
![Page 52: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/52.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/53.jpg)
public class HomeActivity extends BaseActivity implements HomeView {
protected void injectDependencies(AppComponent appComponent) {
appComponent.plus(new HomeModule(this)).inject(this);
}}
![Page 54: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/54.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/55.jpg)
SATISFACTION LEVEL 9001
![Page 56: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/56.jpg)
“If you don’t like testing your product, most likely your customers won’t like to test it
either.”
![Page 57: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/57.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
EXECUTORS MODULE
![Page 58: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/58.jpg)
APP COMPONENT
HOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
EXECUTORS MODULE
![Page 59: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/59.jpg)
APPTESTCOMPONENT
MOCKHOST MODULE
CONVERTER MODULE
CLIENT MODULE
LOGGER MODULE
API MODULE
GSON MODULE
SYNC EXECUTORS
MODULE
![Page 60: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/60.jpg)
@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {
void inject(MyTestApplication app);}
![Page 61: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/61.jpg)
@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {
void inject(MyTestApplication app);}
![Page 62: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/62.jpg)
@Component(modules = { ... MockHostModule.class, SynchronousExecutorsModule.class, ...})@Singletonpublic interface AppTestComponent extends AppComponent {
void inject(MyTestApplication app);}
![Page 63: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/63.jpg)
public class MyTestApplication extends MyApplication implements TestLifecycleApplication {
@Override protected void init() { appComponent = DaggerAppTestComponent.create(); }}
![Page 64: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/64.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/65.jpg)
@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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/66.jpg)
@Testpublic void testEmptyStateNotVisible() { enqueueResponse(“rest-currency-response.json”); btnCurrencyList.performClick(); assertThat(emptyView).isNotVisible();}
![Page 67: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/67.jpg)
THINGS TO REMEMBER
• Orientation change
![Page 68: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/68.jpg)
THINGS TO REMEMBER
• Dagger2 is a powerful tool - make good use of it
• Save yourselves from regression bugs
![Page 69: Building maintainable app](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/69.jpg)
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](https://reader031.vdocument.in/reader031/viewer/2022022203/587431351a28ab72188b7977/html5/thumbnails/70.jpg)
Any questions? KRISTIJ[email protected] @KJURKOVIC
Visit infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum