35
Christian Panadero http://panavtec.me @PaNaVTEC Github - PaNaVTEC My way to clean Android

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

Embed Size (px)

Citation preview

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

Christian Panaderohttp://panavtec.me

@PaNaVTEC Github - PaNaVTEC

My way to clean Android

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

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 @CarlosMChicaiOS Developer @ Selltag Android Developer @ Viagogo

Acknowledgements

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

“My way to clean Android”

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

Why clean architecture?• Independent of Frameworks

• Testable

• Independent of UI

• Independent of Database

• Independent of any external agency

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

Concepts• Command pattern (Invoker, command, receiver)

• Interactors / Use cases

• Abstractions

• Data Source

• Repository

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

Abstraction levels

Presenters

Interactors

Entities

Repository

Data sources

UI

Abstractions

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

The dependency rule

Presenters

Interactors

Entities

Repository

Data sources

UI

Abstractions

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

Thinking in projects• App (UI, DI and implementation details)

• Presentation

• Domain y Entities

• Repository

• Data Sources

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

Project dependenciesApp

Presenters Domain Data

Entities

Repository

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

Flow

View

Presenter

Presenter

Interactor

Interactor

Interactor

Interactor

Repository

Repository

DataSource

DataSource

DataSource

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

UI: MVP

ViewPresenter(s)

Model

Events

Fill the view

Actions Actions output

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

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(); }

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

UI: MVP - Presenterpublic 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();

… }

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

public interface MainView { void showGetContactsError(); void clearData(); }

UI: MVP - Presenter

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

Presentation - Domain

Presenter InteractorInvoker

BusBus IMP

Invoker IMP

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

Presentation - Domainpublic 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(); } }

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

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

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

Repository

NetworkData Source

BDDData Source

RepositoryModel

Data

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

Repository Interfacepublic interface ContactsRepository {

List<Contact> obtainContacts() throws CantRetrieveContactsException; Contact obtain(String md5) throws CannotObtainContactException;

}

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

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; }

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

Data source

Model

Data source Imp

Data source

Mapper

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

Data source Interface

public interface ContactsNetworkDataSource { public List<Contact> obtainContacts() throws ContactsNetworkException, UnknownObtainContactsException; }

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

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

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

Caching Strategypublic 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(); }

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

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(); } }

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

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

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

– Uncle Bob

“Make implementation details swappable”

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

Picassopublic 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); }

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

ErrorManagerpublic 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(); }}

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

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

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

– Uncle Bob

“Clean code. The last programming language”

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

In Uncle Bob we trust

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

Show me the code!https://github.com/PaNaVTEC/Clean-Contacts

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

References

• Fernando Cejas - Clean way

• Jorge Barroso - Arquitectura Tuenti

• Pedro Gomez - Dependency Injection

• Pedro Gomez - Desing patterns

• Uncle Bob - The clean architecture

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

¿Questions?

Christian Panaderohttp://panavtec.me

@PaNaVTEC Github - PaNaVTEC