1. My way to clean Android v2
Christian Panadero
http://panavtec.me
@PaNaVTEC
Github - PaNaVTEC
My Way to clean Android
V2
2. 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
3. My way to clean Android v2
“My way to clean Android”
4. My way to clean Android v2
• Independent of Frameworks
• Testable
• Independent of UI
• Independent of Database
• Independent of any external agency
Why clean architecture?
5. My way to clean Android v2
• Command pattern (Invoker, command, receiver)
• Decorator Pattern
• Interactors / Use cases
• Abstractions
• Data Source
• Repository
• Annotation Processor
Concepts
6. My way to clean Android v2
Abstraction levels
Presenters
Interactors
Entities
Repository
Data sources
UI
Abstractions
7. My way to clean Android v2
The dependency rule
Presenters
Interactors
Entities
Repository
Data sources
UI
Abstractions
8. My way to clean Android v2
• App (UI, DI and implementation details)
• Presentation
• Domain y Entities
• Repository
• Data Sources
Thinking in projects
9. My way to clean Android v2
Project dependencies
App
Presenters Domain Data
Entities
Repository
10. My way to clean Android v2
Flow
View
Presenter
Presenter
Interactor
Interactor
Interactor
Interactor
Repository
Repository
DataSource
DataSource
DataSource
11. My way to clean Android v2
UI: MVP
ViewPresenter(s)
Model
Events
Fill the view
Actions Actions output
12. My way to clean Android v2
UI: MVP - View
public 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();
}
}
13. My way to clean Android v2
UI: MVP - Presenter
public class MainPresenter extends Presenter<MainView> {
public void onResume() {
refreshContactList();
}
public void onRefresh() {
getView().refreshUi();
refreshContactList();
}
}
14. My way to clean Android v2
UI: MVP - Presenter
@ThreadDecoratedView
public interface MainView extends PresenterView {
void showGetContactsError();
void refreshContactsList(List<PresentationContact> contacts);
void refreshUi();
}
public interface PresenterView {
void initUi();
}
prevents spoiler :D
15. 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
16. My way to clean Android v2
onConfigurationChange Hell
public 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();
}
17. My way to clean Android v2
Presentation - Domain
(ASYNC)
Presenter Invoker
Interactor
Output
Invoker IMP
Interactor
18. My way to clean Android v2
Presentation - Domain
(SYNC)
Presenter Invoker
Future
Invoker IMP
Interactor
.get();.cancel();
19. My way to clean Android v2
Presentation - Domain
public 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);
}
}
20. My way to clean Android v2
Domain - Interactor
public 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);
}
}
21. 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();
}
});
}
}
22. 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;
}
}
23. 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);
}
}
@ThreadDecoratedView
public interface MainView extends PresenterView { … }
24. My way to clean Android v2
Repository
Network
Data Source
BDD
Data Source
Repository
Model
Data
25. My way to clean Android v2
Repository Interface
public interface ContactsRepository {
List<Contact> obtainContacts() throws RetrieveContactsException;
Contact obtain(String md5) throws ObtainContactException;
}
27. My way to clean Android v2
Data source
Model
Data source Imp
Data source
Mapper
28. My way to clean Android v2
Data source Interface
public interface ContactsNetworkDataSource {
public List<Contact> obtainContacts() throws ContactsNetworkException …;
}
29. My way to clean Android v2
Data source imp
private 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();
}
}
30. My way to clean Android v2
Caching Strategy
public 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();
}
}
31. 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();
}
}
32. 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
33. My way to clean Android v2
– Uncle Bob
“Make implementation details
swappable”
34. My way to clean Android v2
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;
@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);
}
}
35. My way to clean Android v2
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();
}
}
36. 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
37. My way to clean Android v2
– Uncle Bob
“Clean code. The last programming
language”
38. My way to clean Android v2
In Uncle Bob we trust
39. My way to clean Android v2
https://github.com/PaNaVTEC/Clean-Contacts
Show me the
code!
40. 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
41. My way to clean Android v2
¿Questions?
Christian Panadero
http://panavtec.me
@PaNaVTEC
Github - PaNaVTEC