Top Banner
My way to clean Android v2 Christian Panadero http://panavtec.me @PaNaVTEC Github - PaNaVTEC My Way to clean Android V2
41

My way to clean android v2 English DroidCon Spain

Jul 18, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Christian Panadero

http://panavtec.me @PaNaVTEC

Github - PaNaVTEC

My Way to clean Android V2

Page 2: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Acknowledgements

Fernando Cejas

Jorge Barroso

Pedro Gomez

Sergio Rodrigo

@fernando_cejas

@flipper83

@pedro_g_s

@srodrigoDev

Android developer @ Sound CloudAndroid developer @ Karumi

Cofounder & Android expert @ Karumi

Android developer @ Develapps

Alberto Moraga

Carlos Morera

@albertomoraga

@CarlosMChica

iOS Developer @ Selltag

Android Developer @ Viagogo

Page 3: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

“My way to clean Android”

Page 4: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• Independent of Frameworks

• Testable

• Independent of UI

• Independent of Database

• Independent of any external agency

Why clean architecture?

Page 5: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• Command pattern (Invoker, command, receiver)

• Decorator Pattern

• Interactors / Use cases

• Abstractions

• Data Source

• Repository

• Annotation Processor

Concepts

Page 6: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Abstraction levels

Presenters

Interactors

Entities

Repository

Data sources

UI

Abstractions

Page 7: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

The dependency rule

Presenters

Interactors

Entities

Repository

Data sources

UI

Abstractions

Page 8: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• App (UI, DI and implementation details)

• Presentation

• Domain y Entities

• Repository

• Data Sources

Thinking in projects

Page 9: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Project dependencies

App

Presenters Domain Data

Entities

Repository

Page 10: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Flow

View

Presenter

Presenter

Interactor

Interactor

Interactor

Interactor

Repository

Repository

DataSource

DataSource

DataSource

Page 11: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

UI: MVPViewPresenter(s)

Model

Events

Fill the view

Actions Actions output

Page 12: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

UI: MVP - Viewpublic class MainActivity extends BaseActivity implements MainView { @Inject MainPresenter presenter;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); presenter.attachView(this); } @Override protected void onResume() { super.onResume(); presenter.onResume(); } @Override public void onRefresh() { presenter.onRefresh(); } }

Page 13: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

UI: MVP - Presenterpublic class MainPresenter extends Presenter<MainView> {

public void onResume() { refreshContactList(); } public void onRefresh() { getView().refreshUi(); refreshContactList(); }

}

Page 14: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

UI: MVP - Presenter

@ThreadDecoratedViewpublic interface MainView extends PresenterView { void showGetContactsError(); void refreshContactsList(List<PresentationContact> contacts); void refreshUi(); }

public interface PresenterView { void initUi(); }

prevents spoiler :D

Page 15: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• The configuration change is located in UI module, so solve it in this module

• configurationChange create a new Activity instance

• onRetainCustomNonConfigurationInstance to the rescue!

• Retain the DI graph

onConfigurationChange Hell

Page 16: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

onConfigurationChange Hellpublic abstract class BaseActivity extends ActionBarActivity {

@Override protected void onCreate(Bundle savedInstanceState) { createActivityModule(); } private void createActivityModule() { activityInjector = (ActivityInjector) getLastCustomNonConfigurationInstance(); if (activityInjector == null) { activityInjector = new ActivityInjector(); activityInjector.createGraph(this, newDiModule()); } activityInjector.inject(this); } @Override public Object onRetainCustomNonConfigurationInstance() { return activityInjector; } protected abstract Object newDiModule(); }

Page 17: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Presentation - Domain (ASYNC)

Presenter Invoker

InteractorOutput

Invoker IMP

Interactor

Page 18: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Presentation - Domain (SYNC)

Presenter Invoker

Future

Invoker IMP

Interactor

.get();.cancel();

Page 19: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Presentation - Domainpublic class MainPresenter extends Presenter<MainView> { @Output InteractorOutput<List<Contact>, RetrieveContactsException> output;

public MainPresenter(…) { InteractorOutputInjector.inject(this); }

public void onResume() { interactorInvoker.execute(getContactsInteractor, output); }

@OnError void onContactsInteractorError(RetrieveContactsException data) { getView().showGetContactsError(); }

@OnResult void onContactsInteractor(List<Contact> result) { List<PresentationContact> presentationContacts = listMapper.modelToData(result); getView().refreshContactsList(presentationContacts); }

}

Page 20: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Domain - Interactorpublic class GetContactInteractor implements Interactor<Contact, ObtainContactException> { private ContactsRepository repository; private String contactMd5; public GetContactInteractor(ContactsRepository repository) { this.repository = repository; } public void setData(String contactMd5) { this.contactMd5 = contactMd5; } @Override public Contact call() throws ObtainContactException { return repository.obtain(contactMd5); } }

Page 21: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Bye bye thread Hell!

public class DecoratedMainView implements MainView {

@Override public void showGetContactsError() { this.threadSpec.execute(new Runnable() { @Override public void run() { undecoratedView.showGetContactsError(); } }); }

}

Page 22: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Bye bye thread Hell!public abstract class Presenter<V extends PresenterView> { private V view; private ThreadSpec mainThreadSpec; public Presenter(ThreadSpec mainThreadSpec) { this.mainThreadSpec = mainThreadSpec; } public void attachView(V view) { this.view = ViewInjector.inject(view, mainThreadSpec); } public void detachView() { view = null; } public V getView() { return view; } }

Page 23: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Bye bye thread Hell!

public class MainThreadSpec implements ThreadSpec { Handler handler = new Handler(); @Override public void execute(Runnable action) { handler.post(action); } }

public abstract class Presenter<V extends PresenterView> { public void attachView(V view) { this.view = ViewInjector.inject(view, mainThreadSpec); }

}

@ThreadDecoratedViewpublic interface MainView extends PresenterView { … }

Page 24: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Repository

NetworkData Source

BDDData Source

RepositoryModel

Data

Page 25: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Repository Interface

public interface ContactsRepository { List<Contact> obtainContacts() throws RetrieveContactsException; Contact obtain(String md5) throws ObtainContactException; }

Page 26: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Repository imp@Override public List<Contact> obtainContacts() throws RetrieveContactsException { List<Contact> contacts = null; try { contacts = bddDataSource.obtainContacts(); } catch (ObtainContactsBddException … ce) { try { contacts = networkDataSource.obtainContacts(); bddDataSource.persist(contacts); } catch (UnknownObtainContactsException … ue) { throw new RetrieveContactsException(); } catch (PersistContactsBddException … pe) { pe.printStackTrace(); } } return contacts; }

Page 27: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Data source

Model

Data source Imp

Data source

Mapper

Page 28: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Data source Interface

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

Page 29: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Data source impprivate ContactsApiService apiService; private static final Transformer transformer = new Transformer.Builder().build(ApiContact.class);

@Override public List<Contact> obtainContacts() throws ContactsNetworkException { try { ApiContactsResponse response = apiService.obtainUsers(100); List<ApiContactResult> results = response.getResults(); List<Contact> contacts = new ArrayList<>(); for (ApiContactResult apiContact : results) { contacts.add(transform(apiContact.getUser(), Contact.class)); } return contacts; } catch (Throwable e) { throw new ContactsNetworkException(); } }

Page 30: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Caching Strategypublic interface CachingStrategy<T> { boolean isValid(T data); }

public class TtlCachingStrategy<T extends TtlCachingObject> implements CachingStrategy<T> { private final long ttlMillis; @Override public boolean isValid(T data) { return (data.getPersistedTime() + ttlMillis) > System.currentTimeMillis(); } }

Page 31: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

Caching Strategy@Override public List<Contact> obtainContacts() throws ObtainContactsBddException … { try { List<BddContact> bddContacts = daoContacts.queryForAll(); if (!cachingStrategy.isValid(bddContacts)) { deleteBddContacts(bddContacts); throw new InvalidCacheException(); } ArrayList<Contact> contacts = new ArrayList<>(); for (BddContact bddContact : bddContacts) { contacts.add(transform(bddContact, Contact.class)); } return contacts; } catch (java.sql.SQLException e) { throw new ObtainContactsBddException(); } catch (Throwable e) { throw new UnknownObtainContactsException(); } }

Page 32: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• 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

Repository adavantages

Page 33: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

– Uncle Bob

“Make implementation details swappable”

Page 34: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

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; @Override 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 35: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

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 36: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• 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

Tips

Page 37: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

– Uncle Bob

“Clean code. The last programming language”

Page 38: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

In Uncle Bob we trust

Page 39: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

https://github.com/PaNaVTEC/Clean-Contacts

Show me the code!

Page 40: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

• Fernando Cejas - Clean way

• Jorge Barroso - Arquitectura Tuenti

• Pedro Gomez - Dependency Injection

• Pedro Gomez - Desing patterns

• Uncle Bob - The clean architecture

• PaNaVTEC - Clean without bus

References

Page 41: My way to clean android v2 English DroidCon Spain

My way to clean Android v2

¿Questions?

Christian Panadero

http://panavtec.me @PaNaVTEC

Github - PaNaVTEC