Modularizing Android Applications

1fe615e0a1add5e0c9ce6ac1279c0458?s=47 Mauin
October 26, 2017

Modularizing Android Applications

droidcon London 2017
droidcon Italy 2018

The recording of the talk at droidcon London is available here: http://uk.droidcon.com/skillscasts/10525-modularizing-android-applications#video

Most Android applications consist of one big, monolithic application project. As your application grows this single project can become harder and harder to manage, responsibilities become unclear and build times grow. Marvin will share how you can split up such an Android application into multiple, smaller modules. We will discuss how doing so might change the way you work in your applications codebase and how it can help in setting up clearer interfaces and responsibilities across teams. As modularizing an application changes the way you interact with the build system we will talk about how your build setup and times might change and how different build systems (such as Gradle, Buck and Bazel) interact with multiple modules.

1fe615e0a1add5e0c9ce6ac1279c0458?s=128

Mauin

October 26, 2017
Tweet

Transcript

  1. Modularizing Android Applications Marvin Ramin @Mauin

  2. Modularizing Android Applications Marvin Ramin @Mauin

  3. Motivations to modularize an application

  4. Disclaimer

  5. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" LoginRepository.kt # # !"" content # # # !"" ContentActivity.kt # # # !"" ContentFragment.kt # # # !"" ContentDatabase.kt # # # !"" ApiClient.kt # # !"" feature3 # # !"" feature4 # # !"" apiclient # # !"" ... # # !"" ... # $"" res # !"" drawable # $"" ... # !"" build.gradle !"" gradle $"" settings.gradle
  6. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" LoginRepository.kt # # !"" content # # # !"" ContentActivity.kt # # # !"" ContentFragment.kt # # # !"" ContentDatabase.kt # # # !"" ApiClient.kt # # !"" feature3 # # !"" feature4 # # !"" apiclient # # !"" ... # # !"" ... # $"" res # !"" drawable # $"" ... # !"" build.gradle !"" gradle $"" settings.gradle
  7. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" LoginRepository.kt # # !"" content # # # !"" ContentActivity.kt # # # !"" ContentFragment.kt # # # !"" ContentDatabase.kt # # # !"" ApiClient.kt # # !"" feature3 # # !"" feature4 # # !"" apiclient # # !"" ... # # !"" ... # $"" res # !"" drawable # $"" ... # !"" build.gradle !"" gradle $"" settings.gradle
  8. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" LoginRepository.kt # # !"" content # # # !"" ContentActivity.kt # # # !"" ContentFragment.kt # # # !"" ContentDatabase.kt # # # !"" ApiClient.kt # # !"" feature3 # # !"" feature4 # # !"" apiclient # # !"" ... # # !"" ... # $"" res # !"" drawable # $"" ... # !"" build.gradle !"" gradle $"" settings.gradle
  9. :app

  10. :app login content apiclient

  11. :app login content apiclient apply plugin: ‘com.android.application’

  12. Single Module Application

  13. Single Project Build

  14. Monolith

  15. TODO: Add image of interconnected codebase

  16. Microservices in an Android App

  17. Everything is visible No separation of concerns No clear APIs/responsibilities

  18. Moving towards a modularized codebase

  19. Look at your app

  20. None
  21. None
  22. Look at your codebase

  23. “Libraries” don’t have to be external

  24. Multi-Project Build

  25. :app login content apiclient apply plugin: ‘com.android.application’

  26. :app login content apiclient apply plugin: ‘com.android.application’ settings.gradle include ':app'

  27. :app login content :apiclient apply plugin: ‘com.android.application’

  28. :app login content :apiclient apply plugin: ‘com.android.library’ apply plugin: ‘com.android.application’

  29. :app login content :apiclient apply plugin: ‘com.android.library’ apply plugin: ‘com.android.application’

    implementation project(':apiclient')
  30. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # # !"" apiclient # $"" res # !"" drawable # $"" … !"" build.gradle !"" gradle $"" settings.gradle
  31. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # # !"" apiclient # $"" res # !"" drawable # $"" … !"" build.gradle !"" gradle $"" settings.gradle
  32. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # $"" res # !"" drawable # $"" … !"" apiclient # !"" build.gradle # $"" src !"" build.gradle !"" gradle $"" settings.gradle
  33. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # $"" res # !"" drawable # $"" … !"" apiclient # !"" build.gradle # $"" src !"" build.gradle !"" gradle $"" settings.gradle settings.gradle
  34. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # $"" res # !"" drawable # $"" … !"" apiclient # !"" build.gradle # $"" src !"" build.gradle !"" gradle $"" settings.gradle settings.gradle include ‘:app’, ‘:apiclient’
  35. . !"" app # !"" build.gradle # $"" src #

    $"" main # !"" AndroidManifest.xml # !"" kotlin # # $"" com.some.app # # !"" Application.kt # # !"" login # # # !"" LoginActivity.kt # # # !"" LoginPresenter.kt # # # !"" AccountRepository.kt # # !"" content # $"" res # !"" drawable # $"" … !"" modules # $"" apiclient # !"" build.gradle # $"" src !"" build.gradle !"" gradle $"" settings.gradle settings.gradle include ‘:app’, ‘:modules:apiclient’
  36. :app login content :apiclient apply plugin: ‘com.android.library’ apply plugin: ‘com.android.application’

    implementation project(':apiclient')
  37. :app login content :apiclient apply plugin: ‘java-library’ apply plugin: ‘com.android.application’

    implementation project(':apiclient')
  38. :app login content :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’

    implementation project(':apiclient')
  39. Refactor —> Modularize

  40. Refactor —> Modularize

  41. :app login content :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’

    implementation project(':apiclient')
  42. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    content login
  43. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ content
  44. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') content
  45. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content
  46. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content
  47. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user
  48. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user
  49. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user :utils
  50. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user :utils
  51. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user :utils :rabbithole
  52. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user :utils :rabbithole
  53. :app :apiclient apply plugin: ‘kotlin’ apply plugin: ‘com.android.application’ implementation project(':apiclient')

    :login apply plugin: ‘com.android.library’ implementation project(‘:login') implementation project(‘:apiclient’) content :user :utils :rabbithole
  54. :app login content apiclient apply plugin: ‘com.android.application’

  55. :base content apiclient apply plugin: ‘com.android.library’ :app apply plugin: ‘com.android.application’

    implementation project(‘:base') login
  56. :base content apiclient apply plugin: ‘com.android.library’ :app apply plugin: ‘com.android.application’

    :login implementation project(‘:base') apply plugin: ‘com.android.library’ implementation project(‘:base') implementation project(‘:login’)
  57. :base content apply plugin: ‘com.android.library’ :app apply plugin: ‘com.android.application’ :login

    implementation project(‘:base') :apiclient apply plugin: ‘com.android.library’ apply plugin: ‘kotlin’ implementation project(‘:base') implementation project(‘:login’)
  58. Different (sample) apps

  59. :base apply plugin: ‘com.android.library’ :app :login apply plugin: ‘com.android.library’ :content

    apply plugin: ‘com.android.library’ apply plugin: ‘com.android.application’
  60. :base apply plugin: ‘com.android.library’ :app :login apply plugin: ‘com.android.library’ :content

    apply plugin: ‘com.android.library’
  61. :base apply plugin: ‘com.android.library’ :app :login apply plugin: ‘com.android.library’ :content

    apply plugin: ‘com.android.library’
  62. :base apply plugin: ‘com.android.library’ :app :login apply plugin: ‘com.android.library’ :content

    apply plugin: ‘com.android.library’ :androidwear :instantapp :sample
  63. Each module has a clear API

  64. Each module has a clear API Clear responsibilities between teams

  65. Each module has a clear API Clear responsibilities between teams

    Module maintenance goes up
  66. Each module has a clear API Clear responsibilities between teams

    Module maintenance goes up Compile times go down
  67. Why does it compile faster?

  68. :app

  69. :app Change

  70. :app Change

  71. :app :app Change

  72. :app :app Change Change

  73. :app :app Change Change

  74. Declaring dependencies

  75. compile someDependency

  76. compile someDependency compile someDependency

  77. compile someDependency api someDependency implementation someDependency compile someDependency

  78. api implementation

  79. api implementation :apiclient api ‘com.squareup:okhttp:3.9.0’ :apiclient implementation ‘com.squareup:okhttp:3.9.0’

  80. api implementation :apiclient api ‘com.squareup:okhttp:3.9.0’ :apiclient implementation ‘com.squareup:okhttp:3.9.0’ :app :app

  81. api implementation :apiclient api ‘com.squareup:okhttp:3.9.0’ :apiclient implementation ‘com.squareup:okhttp:3.9.0’ :app :app

    import com.squareup.OkHttpClient import com.squareup.OkHttpClient
  82. api implementation :apiclient api ‘com.squareup:okhttp:3.9.0’ :apiclient implementation ‘com.squareup:okhttp:3.9.0’ :app :app

    import com.squareup.OkHttpClient import com.squareup.OkHttpClient
  83. api implementation :apiclient api ‘com.squareup:okhttp:3.9.0’ :apiclient implementation ‘com.squareup:okhttp:3.9.0’ :app :app

    import com.squareup.OkHttpClient import com.squareup.OkHttpClient
  84. Define your dependencies in one place

  85. Define your dependencies in one place dependencies.gradle file

  86. Define your dependencies in one place dependencies.gradle file Assure dependency

    versions are the same across all modules
  87. Define your dependencies in one place dependencies.gradle file Assure dependency

    versions are the same across all modules Define core dependencies in a base module
  88. // dependencies.gradle ext { androidBuildToolsVersion = '26.0.1' androidCompileSdkVersion = 26

    androidMinSdkVersion = 23 androidTargetSdkVersion = 26 libraries = [ rxJava2 : "io.reactivex.rxjava2:rxjava:2.1.5", ... ] }
  89. // dependencies.gradle ext { androidBuildToolsVersion = '26.0.1' androidCompileSdkVersion = 26

    androidMinSdkVersion = 23 androidTargetSdkVersion = 26 libraries = [ rxJava2 : "io.reactivex.rxjava2:rxjava:2.1.5", ... ] } // build.gradle android { compileSdkVersion androidCompileSdkVersion buildToolsVersion androidBuildToolsVersion ... } dependencies { implementation libraries.rxJava2 }
  90. apply plugin: “com.android.library” android { compileSdkVersion versions.compileSdk defaultConfig { minSdkVersion

    versions.minSdk targetSdkVersion versions.targetSdk versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
  91. apply plugin: “com.android.library” android { compileSdkVersion versions.compileSdk defaultConfig { minSdkVersion

    versions.minSdk targetSdkVersion versions.targetSdk versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } my-base-library.gradle:
  92. // ./module/build.gradle apply from: “my-base-library.gradle” dependencies { … }

  93. Centralize (some) resources

  94. Centralize (some) resources colors, dimens, strings, …

  95. Testing

  96. Dependency Injection

  97. @Component(modules = { ApplicationModule.class, ApiClientModule.class // <- comes from the

    :apiclient module }) public interface ApplicationComponent { }
  98. Navigation

  99. :base apply plugin: ‘com.android.library’ :app apply plugin: ‘com.android.application’ :login apply

    plugin: ‘com.android.library’ :content apply plugin: ‘com.android.library’
  100. :base apply plugin: ‘com.android.library’ :app apply plugin: ‘com.android.application’ :login apply

    plugin: ‘com.android.library’ :content apply plugin: ‘com.android.library’ ?
  101. Start writing new components in separate modules

  102. Start writing new components in separate modules Identify core modules

  103. Start writing new components in separate modules Identify core modules

    Make a plan and extract slowly
  104. Start writing new components in separate modules Identify core modules

    Make a plan and extract slowly Enjoy the multi-module world
  105. Thank you!

  106. Thank you! github.com/Mauin twitter.com/@Mauin