$30 off During Our Annual Pro Sale. View Details »

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. None
  2. None
  3. HELLO I’m Hannes

  4. ANDROID ARCHITECTURE BY EXAMPLE

  5. SLOWED DOWN BY BAD CODE?

  6. FEAR CHANGING CODE!

  7. SOLID Robert C. Martins
 „Uncle Bob“

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

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

    change” Book 
 currentPage() nextPage() print() Book 
 currentPage() nextPage() Printer 
 print(Book)
  10. FUNCTIONAL PROGRAMMING

  11. FOCUS ON WHAT NOT ON HOW

  12. 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;
 }
  13. 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;
 }
  14. 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 }

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

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

  16. 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;
 }
  17. 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()
 }
  18. IMMUTABILITY

  19. None
  20. You want an 
 immutable hot dog

  21. AUTOVALUE @AutoValue public abstract class HotDog { }

  22. AUTOVALUE @AutoValue public abstract class HotDog implements Parcelable { }

  23. AUTOVALUE @AutoValue public abstract class HotDog implements Parcelable {
 @Json

    public abstract double getPrice(); }
  24. AUTOVALUE @AutoValue public abstract class HotDog implements Parcelable {
 @Json

    public abstract double getPrice(); @ColumnName public abstract boolean hasKetchup(); }
  25. 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); } }
  26. MVC Model-View-Controller

  27. MVC Trygve Reenskaug View Model Controller observes changes

  28. None
  29. ARCHITECTURE BY EXAMPLE

  30. 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
  31. None
  32. OMNI-NOTES • Fragments + Activities • AsyncTask with WeakReference as

    Callback • AsyncTask with EventBus • Database + File Storage 2nd June 2016
  33. REDUX Predictable State Container

  34. REDUX • Store • Action • Reducer

  35. REDUX • Store - state container of the Model (immutable)

    • Action • Reducer
  36. REDUX • Store - state container of the Model (immutable)

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

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

    0;
 
 public int add(int x){
 result = x + result;
 return result;
 }
 }
  39. –Robert C. Martins „The root of all evil is the

    assignment statement“
  40. PURE FUNCTION public class PureCalculator {
 
 public int add(int

    x, int y){
 return x + y;
 }
 }
  41. REDUX • Store - state container of the Model (immutable)

    • Action - command to change Model’s state • Reducer - function (action , oldState) 㱺 newState
  42. OmniModel notes […],

  43. OmniModel notes […], AddNoteAction title, description …

  44. 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; }
 …
  45. AddNoteAction title, description … Store reduce(a, m) OmniModel notes […]

  46. Store AddNoteAction title, description … reduce(a, m) OmniModel notes […]

    store.subscribe(this)
  47. Store reduce(a, m) OmniModel notes […] store.subscribe(this) store.dispatch( )

  48. Store reduce(a, m) OmniModel notes […] store.subscribe(this)

  49. Store reduce(a, m) OmniModel notes […] store.subscribe(this) notify(model)

  50. Store reduce(a, m) OmniModel notes […], orderBy: … notify(model) OrderByAction

    order: last modification store.dispatch()
  51. Store reduce(a, m) OmniModel notes […], orderBy: … notify(model) OrderByAction

    order: last modification store.dispatch() notify(model)
  52. REDUX • Immutability • Unidirectional data flow • pure function

  53. 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
  54. None
  55. TELEGRAM • MessageController, ContactsController … • Singletons, no dependency injection

    • Many classes are using these „Controllers“ • Classes with 1000+ lines of code
  56. FLUX

  57. None
  58. ShowListAction

  59. None
  60. MessageStore ContactsStore

  61. PushNotification
 MessageReceivedAction

  62. FLUX • only public getters in Store API • Unidirectional

    data flow
  63. FLUX VS. MVC* • Store ≈ Model • View ≈

    Controller • unidirectional dataflow! *Not Reenskaug MVP
  64. 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
  65. None
  66. TWIDERE • AbstractTask ≈ AsyncTask + WeakReference + EventBus •

    ContentResolver + ContentObserver • Tasks are doing a lot • Fragments & Activities are small ( < 500 Lines of Code) • Dependency Injection (Dagger 2)
  67. None
  68. None
  69. MVVM Model-View-ViewModel

  70. None
  71. public class TweetModel {
 
 public ObservableString message;
 
 public

    ObservableInt stars;
 } Not immutable! Code in XML?
 Error State? Loading Indicator?
  72. REACTIVE PROGRAMMING

  73. java.util.Observable

  74. https://www.youtube.com/watch?v=uNZnftSksYg

  75. RxBus

  76. MVVMC Model-View-ViewModel-Controller

  77. FeedController HomeFeedViewModel loadCommand() : Observable HomeFeedActivity TwitterApi

  78. FeedController HomeFeedViewModel loadCommand() : Observable HomeFeedActivity TwitterApi subscribe to loadCommand()

    ContentProvider
  79. PLAID • https://github.com/nickbutcher/plaid • 5170 Stars on Github • Last

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

    Android Services • Shared Preferences for persistency 2nd June 2016
  82. CLEAN ARCHITECTURE Robert C. Martins

  83. CLEAN ARCHITECTURE Entity Entity Entity Interactor
 Presenter
 View


  84. CLEAN ARCHITECTURE Entity Entity Entity Interactor
 Presenter
 View
 Break the

    Rules! https://publicobject.com/2016/04/20/deliberate-disobedience/
  85. None
  86. MVI Model-View-Intent

  87. cycle.js.org

  88. intent( )

  89. model( intent( ) )

  90. view( model( intent( ) ) )

  91. view( model( intent( ) ) ) new Model displayed by

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

  93. interface SearchView {
 Observable<String> searchIntent();
 void showResult(SearchResult res);
 } class

    SearchResult {
 List<Item> result; boolean loading; boolean error;
 }
  94. 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){ … } }
  95. 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) } }
  96. None
  97. None
  98. None
  99. None
  100. -Someone Smart „If you like the code you have written

    a year ago you haven’t learned enough this year“
  101. REFERENCES • https://github.com/reactjs/redux • http://facebook.github.io/flux/ • https://www.youtube.com/watch?v=uNZnftSksYg&t=757s • https://github.com/bkase/cyklic •

    http://cycle.js.org/
  102. THANK YOU QUESTIONS? hannesdorfmann.com @sockeqwe