Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Software Architecutre by Example

Android Software Architecutre by Example

Comparing some architectural design patterns like MVC, MVP, MVVM, Redux, Flux, MVI and how they could be applied on existing open source applications.

Youtube video: https://www.youtube.com/watch?v=7t7MN8e1W7s

Hannes Dorfmann

June 02, 2016
Tweet

More Decks by Hannes Dorfmann

Other Decks in Programming

Transcript

  1. SINGLE RESPONSIBILITY “A class should only have one reason to

    change” Book 
 currentPage() nextPage() print()
  2. SINGLE RESPONSIBILITY “A class should only have one reason to

    change” Book 
 currentPage() nextPage() print() Book 
 currentPage() nextPage() Printer 
 print(Book)
  3. public Map<String, Integer> xxx(String[] words){
 Map<String, Integer> countMap = new

    HashMap();
 
 for (String word : words ) {
 Integer count = countMap.get(word);
 if (count == null)
 countMap.put(word, 1);
 else
 countMap.put(word, count + 1);
 }
 return countMap;
 }
  4. public Map<String, Integer> countWords(String[] words){
 Map<String, Integer> countMap = new

    HashMap();
 
 for (String word : words ) {
 Integer count = countMap.get(word);
 if (count == null)
 countMap.put(word, 1);
 else
 countMap.put(word, count + 1);
 }
 return countMap;
 }
  5. fun countWords(words: Array<String>) : Map<String, Int> { val grouped: Map<String,

    List<String>> = words.groupBy { word -> word }
 val countMap: Map<String, Int> = grouped.mapValues { entry -> entry.value.size }
 return countMap }

  6. fun countWords(words: Array<String>) : Map<String, Int> { return words.groupBy {

    word -> word } .mapValues { entry -> entry.value.size } }

  7. public Pair<String, Integer> mostFrequent( String[] words, int threshold) {
 Pair<String,

    Integer> mostFrequent = null;
 Map<String, Integer> countMap = countWords( words );
 
 for (Map.Entry<String, Integer> entry : countMap.entrySet() ) {
 if (entry.getValue() < threshold)
 continue;
 if (mostFrequent == null || entry.getValue() > mostFrequent.second)
 mostFrequent = new Pair(entry.getKey(), entry.getValue());
 }
 
 return mostFrequent;
 }
  8. fun mostFrequent(words: Array<String>, threshold: Int) : Pair<String, Int> { return

    countWords(words) // Map<String, Int>
 .toList() // List<Pair<String, Int>>
 .sortedBy { pair -> pair.second }
 .filter { pair -> pair.second > threshold }
 .first()
 }
  9. AUTOVALUE @AutoValue public abstract class HotDog implements Parcelable {
 @Json

    public abstract double getPrice(); @ColumnName public abstract boolean hasKetchup(); }
  10. AUTOVALUE @AutoValue public abstract class HotDog implements Parcelable {
 @Json

    public abstract double getPrice(); @ColumnName public abstract boolean hasKetchup(); public abstract static HotDog(double p, boolean k) { return new AutoValue_HotDog(p, k); } }
  11. OMNI-NOTES • https://github.com/federicoiosue/Omni-Notes • 506 Stars on Github • Last

    commit: 31st of May 2016 • 4,4 Rating • 100.000 - 500.000 installs 2nd June 2016
  12. OMNI-NOTES • Fragments + Activities • AsyncTask with WeakReference as

    Callback • AsyncTask with EventBus • Database + File Storage 2nd June 2016
  13. REDUX • Store - state container of the Model (immutable)

    • Action - command to change Model’s state • Reducer
  14. REDUX • Store - state container of the Model (immutable)

    • Action - command to change Model’s state • Reducer - function (action , oldState) 㱺 newState
  15. PURE FUNCTION public class Calculator {
 
 int result =

    0;
 
 public int add(int x){
 result = x + result;
 return result;
 }
 }
  16. REDUX • Store - state container of the Model (immutable)

    • Action - command to change Model’s state • Reducer - function (action , oldState) 㱺 newState
  17. OmniModel notes […] AddNoteAction title, description … public OmniModel reduce(Action

    a, OmniModel old){
 if (a instance AddNoteAction){ OmniModel newModel = oldModel.withNotes(old.notes + a.newNode) return newModel; }
 …
  18. Store reduce(a, m) OmniModel notes […], orderBy: … notify(model) OrderByAction

    order: last modification store.dispatch() notify(model)
  19. TELEGRAM • https://github.com/DrKLO/Telegram • 4999 Stars on Github • Last

    commit: 26th of May 2016 • 4,2 Rating • 100.000.000 - 500.000.000 installs 2nd June 2016
  20. TELEGRAM • MessageController, ContactsController … • Singletons, no dependency injection

    • Many classes are using these „Controllers“ • Classes with 1000+ lines of code
  21. FLUX VS. MVC* • Store ≈ Model • View ≈

    Controller • unidirectional dataflow! *Not Reenskaug MVP
  22. TWIDERE • https://github.com/TwidereProject/Twidere-Android • 873 Stars on Github • Last

    commit: 16th of May 2016 • 4,1 Rating • 100.000 - 500.000 installs 2nd June 2016
  23. TWIDERE • AbstractTask ≈ AsyncTask + WeakReference + EventBus •

    ContentResolver + ContentObserver • Tasks are doing a lot • Fragments & Activities are small ( < 500 Lines of Code) • Dependency Injection (Dagger 2)
  24. public class TweetModel {
 
 public ObservableString message;
 
 public

    ObservableInt stars;
 } Not immutable! Code in XML?
 Error State? Loading Indicator?
  25. PLAID • https://github.com/nickbutcher/plaid • 5170 Stars on Github • Last

    commit: 1st of June 2016 • 5.000 - 10.000 installs 2nd June 2016
  26. PLAID • No clear Separation of responsibilities • AsyncTask •

    Android Services • Shared Preferences for persistency 2nd June 2016
  27. CLEAN ARCHITECTURE Entity Entity Entity Interactor
 Presenter
 View
 Break the

    Rules! https://publicobject.com/2016/04/20/deliberate-disobedience/
  28. view( model( intent( ) ) ) new Model displayed by

    View Intent changes Model UI Events start intents
  29. interface SearchView {
 Observable<String> searchIntent();
 void showResult(SearchResult res);
 } class

    SearchResult {
 List<Item> result; boolean loading; boolean error;
 }
  30. interface SearchView {
 Observable<String> searchIntent();
 void showResult(SearchResult res);
 } class

    SearchResult {
 List<Item> result; boolean loading; boolean error;
 } class SearchEngine {
 Observable<SearchResult> search(Observable<String> s){ … } }
  31. interface SearchView {
 Observable<String> searchIntent();
 void showResult(SearchResult res);
 } class

    SearchResult {
 List<Item> result; boolean loading; boolean error;
 } class SearchEngine {
 Observable<SearchResult> search(Observable<String> s){ … } } class SearchPresenter {
 void attachView(SearchView v){ searchEngine.search(v.searchIntent()) .subscribe(v::showResult) } }
  32. -Someone Smart „If you like the code you have written

    a year ago you haven’t learned enough this year“