Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

My way to clean android (EN) - Android day salamanca edition

2,833 views

Published on

My way to clean android implementation

Published in: Technology

My way to clean android (EN) - Android day salamanca edition

  1. 1. Christian Panadero http://panavtec.me @PaNaVTEC Github - PaNaVTEC My way to clean Android
  2. 2. Fernando Cejas Jorge Barroso Pedro Gomez Sergio Rodrigo @fernando_cejas @flipper83 @pedro_g_s @srodrigoDev Android developer @ Sound Cloud Android developer @ Tuenti Cofounder & Android expert @ Karumi Android developer @ Develapps Alberto Moraga Carlos Morera @albertomoraga @CarlosMChica iOS Developer @ Selltag Android Developer @ Viagogo Acknowledgements
  3. 3. “My way to clean Android”
  4. 4. Why clean architecture? • Independent of Frameworks • Testable • Independent of UI • Independent of Database • Independent of any external agency
  5. 5. Concepts • Command pattern (Invoker, command, receiver) • Interactors / Use cases • Abstractions • Data Source • Repository
  6. 6. Abstraction levels Presenters Interactors Entities Repository Data sources UI Abstractions
  7. 7. The dependency rule Presenters Interactors Entities Repository Data sources UI Abstractions
  8. 8. Thinking in projects • App (UI, DI and implementation details) • Presentation • Domain y Entities • Repository • Data Sources
  9. 9. Project dependencies App Presenters Domain Data Entities Repository
  10. 10. Flow View Presenter Presenter Interactor Interactor Interactor Interactor Repository Repository DataSource DataSource DataSource
  11. 11. UI: MVP ViewPresenter(s) Model Events Fill the view Actions Actions output
  12. 12. UI: MVP - View public class MainActivity extends BaseActivity implements MainView { @Inject MainPresenter presenter; @Override protected void onResume() { super.onResume(); presenter.onResume(); } @Override public void onRefresh() { presenter.onRefresh(); }
  13. 13. UI: MVP - Presenter public class MainPresenter extends Presenter { private MainView mainView; private Bus bus; public void onResume() { bus.register(this); } public void onPause() { bus.unregister(this); } public void onRefresh() { mainView.clearData(); … }
  14. 14. public interface MainView { void showGetContactsError(); void clearData(); } UI: MVP - Presenter
  15. 15. Presentation - Domain Presenter InteractorInvoker Bus Bus IMP Invoker IMP
  16. 16. Presentation - Domain public class MainPresenter extends Presenter { public void onCreate() { interactorInvoker.execute(getContactsInteractor); } public void onEvent(GetContactsEvent event) { if (event.getError() == null) { List<PresentationContact> contacts = event.getContacts(); mainView.refreshContactsList(contacts); } else { mainView.showGetContactsError(); } }
  17. 17. public class GetContactsInteractor implements Interactor { private Bus bus; private ContactsRepository repository; @Override public void execute() { GetContactsEvent event = new GetContactsEvent(); try { List<Contact> contacts = repository.obtainContacts(); event.setContacts(contacts); } catch (RetrieveContactsException e) { event.setError(e); } bus.post(event); } } Domain - Interactor
  18. 18. Repository Network Data Source BDD Data Source Repository Model Data
  19. 19. Repository Interface public interface ContactsRepository { List<Contact> obtainContacts() throws CantRetrieveContactsException; Contact obtain(String md5) throws CannotObtainContactException; }
  20. 20. Repository imp @Override public List<Contact> obtainContacts() throws RetrieveContactsException { List<Contact> contacts = null; try { contacts = bddDataSource.obtainContacts(); } catch (ObtainContactsBddException | InvalidCacheException e) { try { contacts = networkDataSource.obtainContacts(); bddDataSource.persist(contacts); } catch (UnknownObtainContactsException | ContactsNetworkException) { throw new RetrieveContactsException(); } catch (PersistContactsBddException) { e.printStackTrace(); } } return contacts; }
  21. 21. Data source Model Data source Imp Data source Mapper
  22. 22. Data source Interface public interface ContactsNetworkDataSource { public List<Contact> obtainContacts() throws ContactsNetworkException, UnknownObtainContactsException; }
  23. 23. private ContactsApiService apiService; private static final ApiContactMapper mapper = new ApiContactMapper(); @Override public List<Contact> obtainContacts() throws ContactsNetworkException { try { ApiContactsResponse apiContactsResponse = apiService.obtainUsers(100); List<ApiContactResult> results = apiContactsResponse.getResults(); <MAP APICONTACTS TO CONTACTS> return contacts; } catch (Throwable e) { throw new ContactsNetworkException(); } } Data source imp
  24. 24. Caching Strategy public interface CachingStrategy<T> { boolean isValid(T data); } public TtlCachingStrategy(int ttl, TimeUnit timeUnit) { ttlMillis = timeUnit.toMillis(ttl); } @Override public boolean isValid(T data) { return (data.getPersistedTime() + ttlMillis) > System.currentTimeMillis(); }
  25. 25. Caching Strategy @Override public List<Contact> obtainContacts() throws ObtainContactsBddException, UnknownObtainContactsException, InvalidCacheException { try { List<BddContact> bddContacts = daoContacts.queryForAll(); if (!cachingStrategy.isValid(bddContacts)) { deleteBddContacts(cachingStrategy.candidatesToPurgue(bddContacts)); throw new InvalidCacheException(); } ArrayList<Contact> contacts = new ArrayList<>(); for (BddContact bddContact : bddContacts) { contacts.add(transformer.transform(bddContact, Contact.class)); } return contacts; } catch (java.sql.SQLException e) { throw new ObtainContactsBddException(); } catch (Throwable e) { throw new UnknownObtainContactsException(); } }
  26. 26. Repository adavantages • Bussines logic doesn’t know where the data came from • It’s easy to change data source implementation • If you change the data sources implementation the business logic is not altered
  27. 27. – Uncle Bob “Make implementation details swappable”
  28. 28. Picasso public interface ImageLoader { public void load(String url, ImageView imageView); public void loadCircular(String url, ImageView imageView); } public class PicassoImageLoader implements ImageLoader { private Picasso picasso; public PicassoImageLoader(Picasso picasso) { this.picasso = picasso; } public void load(String url, ImageView imageView) { picasso.load(url).into(imageView); } @Override public void loadCircular(String url, ImageView imageView) { picasso.load(url).transform(new CircleTransform()).into(imageView); }
  29. 29. ErrorManager public interface ErrorManager { public void showError(String error); } public class SnackbarErrorManagerImp implements ErrorManager { @Override public void showError(String error) { SnackbarManager.show(Snackbar.with(activity).text(error)); } } public class ToastErrorManagerImp implements ErrorManager { @Override public void showError(String error) { Toast.makeText(activity, error, Toast.LENGTH_LONG).show(); } }
  30. 30. Tips • ALWAYS Depend upon abstractions, NEVER depend upon concretions • Use a good naming, if there's a class you've created and the naming does not feel right, most probably it is wrong modeled. • Create new shapes using the initial dartboard to ensure that it's placed on the corresponding layer
  31. 31. – Uncle Bob “Clean code. The last programming language”
  32. 32. In Uncle Bob we trust
  33. 33. Show me the code! https://github.com/PaNaVTEC/Clean-Contacts
  34. 34. References • Fernando Cejas - Clean way • Jorge Barroso - Arquitectura Tuenti • Pedro Gomez - Dependency Injection • Pedro Gomez - Desing patterns • Uncle Bob - The clean architecture
  35. 35. ¿Questions? Christian Panadero http://panavtec.me @PaNaVTEC Github - PaNaVTEC

×