Tips for implementing Android Dynamic Feature Module

Tips for implementing Android Dynamic Feature Module

At CA.apk#9, I have talked about the merit for implementing features with Dynamic Feature Module (DFM), and shared some tips for implementing features with DFM.

---

Official Docs
* Android App Bundles | Android Developers
https://developer.android.com/guide/app-bundle

* About Dynamic Delivery  |  Android Developers
https://developer.android.com/guide/app-bundle/dynamic-delivery

* About Conditional Delivery | Android Developers
https://developer.android.com/guide/app-bundle/conditional-delivery

---

Other Presentations regarding DFM(JP)
* Dynamic Feature Module in Practice
https://speakerdeck.com/star_zero/droidkaigi-2020

---

Other Links
* R8 & DataBinding with DFM
https://stackoverflow.com/questions/54404492/proguard-with-appbundle-and-data-binding-illegalstateexception
https://issuetracker.google.com/issues/139046605

* Delivery Config - Apk
https://stackoverflow.com/questions/54844007/how-to-generate-full-apk-file-including-dynamic-feature-module

* Jacoco Config
https://github.com/arturdm/jacoco-android-gradle-plugin/issues/80
https://github.com/vanniktech/gradle-android-junit-jacoco-plugin/pull/158

F9856cc7a15ed2cb9e6ebfab41fdf1cf?s=128

Shohei Kawano

July 14, 2020
Tweet

Transcript

  1. DFM Tips @shoheikawano @_shoheikawano

  2. Content ・Why DFM? ・Tips for adding feature with DFM ・Conclusion


  3. Why DFM?

  4. Why DFM? ・”Ability to customize how and when the feature

    to be downloaded onto devices running Android 5.0”  ・Reduce initial App install size https://developer.android.com/guide/app-bundle/dynamic-delivery
  5. Why DFM? 0 17 34 Without DFM with DFM Initial

    App Intall Size ※Estimate
  6. Tips

  7. Tips Delivery Config Jacoco Config R8 & DataBinding

  8. Tips R8 & DataBinding Delivery Config Jacoco Config

  9. R8 & DataBinding

  10. R8 & DataBinding // app/build.gradle release { minifyEnabled = true

    }
  11. R8 & DataBinding // dynamic-feature-module/build.gradle buildFeatures { dataBinding = true

    } // app/build.gradle release { minifyEnabled = true }
  12. R8 & DataBinding // dynamic-feature-module/build.gradle buildFeatures { dataBinding = true

    } // dynamic-feature-module/DynamicActivity.kt DataBindingUtil.setContentView(this, layoutId) // app/build.gradle release { minifyEnabled = true }
  13. R8 & DataBinding // dynamic-feature-module/build.gradle buildFeatures { dataBinding = true

    } // dynamic-feature-module/DynamicActivity.kt DataBindingUtil.setContentView(this, layoutId) Release Build // app/build.gradle release { minifyEnabled = true }
  14. R8 & DataBinding //dynamic-feature-module/build.gradle buildFeatures { dataBinding = true }

    // DynamicActivity.kt DataBindingUtil.setContentView(this, layoutId) Release Build //app/build.gradle release { minifyEnabled = true } RuntimeException java.lang.RuntimeException: Unable to start activity ComponentInfo{...DynamicActivity}: java.lang.IllegalStateException: DataBindingUtil.setConte… R.layout.dynamic_activity) must not be null Caused by: java.lang.IllegalStateException: DataBindingUtil.setConte… R.layout.dynamic_activity) must not be null at DynamicActivity.onCreate(DynamicActivity.kt:38)
  15. # dynamic-feature-module/proguard-rules-dynamic-features.pro -keep class your.dfm.package.name.DataBinderMapperImpl { *; } https://issuetracker.google.com/issues/139046605 R8

    & DataBinding https://stackoverflow.com/questions/54404492/proguard-with-appbundle-and-data-binding-illegalstateexception // dynamic-feature-module/build.gradle buildTypes { release { proguardFiles 'proguard-rules-dynamic-features.pro' } }
  16. Tips Delivery Config Jacoco Config R8 & DataBinding

  17. Tips Delivery Config Jacoco Config R8 & DataBinding

  18. Delivery Config - Install

  19. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:on-demand /> </dist:delivery>

    </dist:module> Delivery Config - Install
  20. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:on-demand /> </dist:delivery>

    </dist:module> $ ./gradlew install{Flavor}Debug or Delivery Config - Install
  21. <!-- dynamic-feature-module/AndroidManifest.xml —> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:on-demand /> </dist:delivery>

    </dist:module> $ ./gradlew install{Flavor}Debug or Delivery Config - Install ActivityNotFoundException android.content.ActivityNotFoundException: Unable to find explicit activity class {com.your.dfm.package.name.DynamicActivity}; have you declared this activity in your AndroidManifest.xml?
  22. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:on-demand /> </dist:delivery>

    </dist:module> Delivery Config - Install
  23. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:install-time /> </dist:delivery>

    </dist:module> Delivery Config - Install
  24. Delivery Config - Apk

  25. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:delivery> <dist:install-time /> </dist:delivery>

    </dist:module> Delivery Config - Apk
  26. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk
  27. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk https://developer.android.com/guide/app-bundle/dynamic-delivery ・Include module in multi-APKs for device running Android 4.4 or lower
  28. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk https://developer.android.com/guide/app-bundle/dynamic-delivery ・Include module in the universal APK using bundletool ・Include module in multi-APKs for device running Android 4.4 or lower
  29. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk
  30. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk $ ./gradlew package{Flavor}DebugUniversalApk $ ./gradlew package{Flavor}ReleaseUniversalApk https://stackoverflow.com/questions/54844007/how-to-generate-full-apk-file-including-dynamic-feature-module
  31. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk Debugging → install-time Releasing → on-demand
  32. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk
  33. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time /> </dist:delivery> </dist:module> Delivery Config - Apk
  34. Delivery Config - Apk <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name">

    <dist:fusing dist:include="true" /> <dist:delivery> <dist:on-demand /> <dist:install-time> <dist:conditions> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module>
  35. <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name"> <dist:fusing dist:include="true" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk https://developer.android.com/guide/app-bundle/conditional-delivery Requirements for DFM to be downloaded during app install
  36. Delivery Config - Apk <!-- dynamic-feature-module/AndroidManifest.xml —-> <dist:module dist:instant="false" dist:title="@string/dfm_module_name">

    <dist:fusing dist:include="true" /> <dist:delivery> <dist:on-demand /> <dist:install-time> <dist:conditions> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module>
  37. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="dfm_module_name"> <dist:fusing dist:include="${include_fusing}" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> <dist:user-countries dist:exclude="${exclude_install_time}"> <dist:country dist:code="JP" /> </dist:user-countries> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk
  38. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="dfm_module_name"> <dist:fusing dist:include="${include_fusing}" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> <dist:user-countries dist:exclude="${exclude_install_time}"> <dist:country dist:code="JP" /> </dist:user-countries> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk Do not include DFM during install-time if ‘exclude’ is true
  39. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="dfm_module_name"> <dist:fusing dist:include="${include_fusing}" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> <dist:user-countries dist:exclude="${exclude_install_time}"> <dist:country dist:code="JP" /> </dist:user-countries> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk Include DFM during install-time for this country code
  40. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="dfm_module_name"> <dist:fusing dist:include="${include_fusing}" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> <dist:user-countries dist:exclude="${exclude_install_time}"> <dist:country dist:code="JP" /> </dist:user-countries> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk Manifest Placeholder to toggle based on Product Flavor
  41. <!-- dynamic-feature-module/AndroidManifest.xml -—> <dist:module dist:instant="false" dist:title="dfm_module_name"> <dist:fusing dist:include="${include_fusing}" /> <dist:delivery>

    <dist:on-demand /> <dist:install-time> <dist:conditions> <dist:user-countries dist:exclude="${exclude_install_time}"> <dist:country dist:code="JP" /> </dist:user-countries> </dist:conditions> </dist:install-time> </dist:delivery> </dist:module> Delivery Config - Apk
  42. // dynamic-feature-module/build.gradle productFlavors { flavor1 { manifestPlaceholders = [include_fusing: true,

    exclude_install_time: false] } flavor2 { manifestPlaceholders = [include_fusing: false, exclude_install_time: true] } } ... Delivery Config - Apk
  43. Tips Delivery Config Jacoco Config R8 & DataBinding

  44. Tips Delivery Config Jacoco Config R8 & DataBinding

  45. Jacoco Config // root/build.gradle dependencies { classpath ‘com.dicedmelon.gradle:jacoco-android:0.1.4' } //

    dynamic-feature-module/build.gradle apply plugin: 'jacoco-android'
  46. Jacoco Config // root/build.gradle dependencies { classpath ‘com.dicedmelon.gradle:jacoco-android:0.1.4' } //

    dfm/build.gradle apply plugin: 'jacoco-android' * What went wrong: A problem occurred evaluating script. > Failed to apply plugin [id 'jacoco-android'] > You must apply the Android plugin or the Android library plugin before using the jacoco-android plugin
  47. Jacoco Config // root/build.gradle dependencies { classpath ‘com.dicedmelon.gradle:jacoco-android:0.1.4' } //

    dynamic-feature-module/build.gradle apply plugin: 'jacoco-android'
  48. Jacoco Config // root/build.gradle dependencies { classpath ‘com.dicedmelon.gradle:jacoco-android:0.1.4' } //

    dynamic-feature-module/build.gradle apply plugin: 'jacoco-android'
  49. Jacoco Config // root/build.gradle dependencies { classpath ‘com.dicedmelon.gradle:jacoco-android:0.1.4' } //

    dynamic-feature-module/build.gradle apply plugin: 'jacoco-android'
  50. Jacoco Config .. Alternative?

  51. Conclusion

  52. Tips Why DFM? — To reduce App Install Size —

    Keep DataBinderMapperImpl — Configure delivery options Carefully — Use fusing / include option for APK — Use manifest placeholder if necessary — Some Jacoco android gradle plugin(s) just do not work with DFM Conclusion
  53. DFM Tips @shoheikawano @_shoheikawano Thank you!