My way to clean android V2

Post on 16-Jul-2015

870 views 1 download

Transcript of My way to clean android V2

My way to clean Android v2

Christian Panadero

http://panavtec.me @PaNaVTEC

Github - PaNaVTEC

My Way to clean Android V2

My way to clean Android v2

Agradecimientos

Fernando Cejas

Jorge Barroso

Pedro Gomez

Sergio Rodrigo

@fernando_cejas

@flipper83

@pedro_g_s

@srodrigoDev

Android developer en Sound CloudAndroid developer en Karumi

Cofounder & Android expert en Karumi

Android developer en Develapps

Alberto Moraga

Carlos Morera

@albertomoraga

@CarlosMChica

iOS Developer en Selltag

Android Developer en Viagogo

My way to clean Android v2

“My way to clean Android”

My way to clean Android v2

• Desacoplado de los frameworks

• Testable

• Desacoplado de la UI

• Desacoplado de BDD

• Desacoplado de cualquier detalle de implementación

¿Por qué Clean?

My way to clean Android v2

• Patrón command (Invoker, command, receiver)

• Patrón decorator

• Interactors / Casos de uso

• Abstracciones

• Data Source

• Repository

• Procesador de Anotaciones

Conceptos

My way to clean Android v2

Niveles de abstracción

Presenters

Interactors

Entidades

Repository

Data sources

UI

Abstractions

My way to clean Android v2

Regla de dependencias

Presenters

Interactors

Entidades

Repository

Data sources

UI

Abstractions

My way to clean Android v2

• App (UI, DI y detalles de implementación)

• Presentation

• Domain y Entities

• Repository

• Data Sources

Modelando el proyecto

My way to clean Android v2

Dependencias del proyecto

App

Presenters Domain Data

Entities

Repository

My way to clean Android v2

Flow

View

Presenter

Presenter

Interactor

Interactor

Interactor

Interactor

Repository

Repository

DataSource

DataSource

DataSource

My way to clean Android v2

UI: MVPViewPresenter(s)

Model

Eventos

Rellena la vista

Acciones Resultados de esas acciones

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

My way to clean Android v2

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

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

}

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

My way to clean Android v2

• El problema de las rotaciones está en la capa de UI

• configurationChange recrea toda la Activity

• onRetainCustomNonConfigurationInstance al rescate!

• Retener el grafo de dagger

onConfigurationChange Hell

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

My way to clean Android v2

Presentation - Domain (ASYNC)

Presenter Invoker

InteractorOutput

Invoker IMP

Interactor

My way to clean Android v2

Presentation - Domain (SYNC)

Presenter Invoker

Future

Invoker IMP

Interactor

.get();.cancel();

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

}

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

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

}

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

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 { … }

My way to clean Android v2

Repository

NetworkData Source

BDDData Source

RepositoryModel

Data

My way to clean Android v2

Repository Interface

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

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

My way to clean Android v2

Data source

Model

Data source Imp

Data source

Mapper

My way to clean Android v2

Data source Interface

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

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

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

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

My way to clean Android v2

• La lógica de negocio no sabe de donde vienen los datos.

• Fácil de cambiar la implementación de los orígenes de datos.

• En caso de cambio de orígenes de datos la lógica de negocio no se ve alterada.

Ventajas de Repository

My way to clean Android v2

– Uncle Bob

“Make implementation details swappable”

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

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

My way to clean Android v2

• Trabaja siempre con abstracciones nunca con concreciones.

• Usa un buen naming, si ves que hay alguna figura que has creado que no sabes que nombre poner, seguramente esté mal modelada.

• Si creas nuevas figuras usa la diana inicial para asegurarte que las creas en la capa correspondiente

Consejos

My way to clean Android v2

– Uncle Bob

“Clean code. The last programming language”

My way to clean Android v2

In Uncle Bob we trust

My way to clean Android v2

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

Show me the code!

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

Referencias

My way to clean Android v2

¿Preguntas?

Christian Panadero

http://panavtec.me @PaNaVTEC

Github - PaNaVTEC