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

Relevance of code minification in a performance world

Ce1ca64f3265f01a8718a622427f0a1d?s=47 Jitin
October 27, 2021

Relevance of code minification in a performance world

R8 and Proguard are Android developer's go-to tool for code minification. As a developer, inconsistent proguard rules can affect your application in more ways than you know. This talk will focus on code minification and it's additional impacts, divided into 3 parts
- Small introduction to code obfuscation and minification.
- Introduction to proguard rules, common mistakes and how to avoid them.
- A deep dive on how performance of app is impacted by proguard rules.

Ce1ca64f3265f01a8718a622427f0a1d?s=128

Jitin

October 27, 2021
Tweet

More Decks by Jitin

Other Decks in Programming

Transcript

  1. Relevance of code minification in a performance world Jitin Sharma

    GDE Android, Gojek Android Worldwide jitinsharma.com @_jitinsharma
  2. What we’ll discuss • Code obfuscation and minification • Introduction

    to obfuscation tools • Proguard rules • Performance impacts of code minification • Common mistakes and how to avoid them
  3. Obfuscation

  4. Life of code Kotlin Class dex Source code Bytecode Bytecode

  5. Life of code Kotlin Class dex Source code Bytecode resources

    + apk
  6. Life of code Kotlin Class dex Source code Bytecode Bytecode

    • dedexer • smali
  7. Life of code Kotlin Class dex Source code Bytecode Bytecode

    jadx
  8. Obfuscation class Repository viewModel.fetchData() fun matchUser() val loggedIn = false

    package com.awesome.app class a c.zz() fun rt() val y = false package com.g.f
  9. minification

  10. Minification fun main() { fetchUser() } fun fetchUser() { fetchProfile()

    } fun fetchProfile() { displayProfile() } fun displayProfile() { .. } main() fetchUser() fetchProfile() displayProfile()
  11. Minification fun main() { fetchUser() } fun fetchUser() { //

    fetchProfile() } fun fetchProfile() { displayProfile() } fun displayProfile() { .. } main() fetchUser() fetchProfile() displayProfile()
  12. Minification fun main() { fetchUser() } fun fetchUser() { //

    fetchProfile() } fun fetchProfile() { displayProfile() } fun displayProfile() { .. } main() fetchUser() fetchProfile() displayProfile()
  13. Minification Activities Service Application Receivers Code

  14. Minification Activities Service Application Receivers Code resources

  15. Tools

  16. Proguard Dexguard R8

  17. Android Toolchain Code (source code + libraries) Obfuscation + minification

    D8/R8 minified dex AGP Toolchain (removes unused resources) manifest, resources apk dex/IR
  18. Android Toolchain Code (source code + libraries) dex/IR minified dex

    AGP Toolchain (removes unused resources) manifest, resources ResourceUsageAnalyzer.java apk Obfuscation + minification D8/R8
  19. Rules

  20. Rules fun main() { val clazz = Class.forName() val method

    = clazz.getDeclaredMethod() method.invoke() } main() method()
  21. Rules fun main() { val clazz = Class.forName() val method

    = clazz.getMethod() method.invoke() } main() method() Classes Fields Interfaces Functions
  22. Rules - keep class … - keepclassmembers … - keep

    attributes … - dontwarn …
  23. Optimisation and Performance

  24. Test bed project ~20 modules, 100 classes, 6000 methods androidx

    Rxjava Glide Kotlin AGP 4.1 D8/R8
  25. Comparison App Type Size App Launch Mode Non Minified 11.8MB

    596.6ms Release Minified 6.5MB 367.9ms Release Minified + Shrinked 6.4MB 361.3ms Release
  26. 11.8 mb 596.6 ms 6.4 mb (45%) 361.3 ms (39%)

    Non Minified Minified
  27. Optimisations

  28. Code Inlining fun myBranchedFunction() { if (BuildConfig.DEBUG) { debugMethod() }

    else { releaseMethod() } } fun myBranchedFunction() { releaseMethod() }
  29. Code Inlining fun myBranchedFunction() { releaseMethod() } fun main() {

    myBranchedFunction() myFunction2() }
  30. Code Inlining fun myBranchedFunction() { releaseMethod() } fun main() {

    releaseMethod() myFunction2() }
  31. Code Inlining if (Build.VERSION.SDK_INT >= 21) { window.statusBarColor = ..

    } if (Build.VERSION.SDK_INT >= 28) { val displayCutOut = decorView.rootWindowInsets.displayCutout ... } minSdk 19 if (Build.VERSION.SDK_INT >= 21) { window.statusBarColor = .. } if (Build.VERSION.SDK_INT >= 28) { val displayCutOut = decorView.rootWindowInsets.displayCutout ... }
  32. Code Inlining if (Build.VERSION.SDK_INT >= 21) { window.statusBarColor = ..

    } if (Build.VERSION.SDK_INT >= 28) { val displayCutOut = decorView.rootWindowInsets.displayCutout ... } minSdk 21 window.statusBarColor = .. if (Build.VERSION.SDK_INT >= 28) { val displayCutOut = decorView.rootWindowInsets.displayCutout ... }
  33. Code Inlining if (Build.VERSION.SDK_INT >= 21) { window.statusBarColor = ..

    } if (Build.VERSION.SDK_INT >= 28) { val displayCutOut = decorView.rootWindowInsets.displayCutout ... } minSdk 28 window.statusBarColor = .. val displayCutOut = decorView.rootWindowInsets.displayCutout ...
  34. Code Inlining fun trim(String name) { Intrinsics.checkParameterIsNotNull(name) name(…) } fun

    trim(String name) { if (name != null) { name(…) } else { // exception } }
  35. App Startup 596.6 ms 361.3 ms (39%) Non Minified Minified

  36. App Startup Non Minified Minified

  37. App Startup Non Minified Minified Dex load: 2.96 ms Dex

    load: 0.07 ms
  38. Systrace Event Non minified Minified Mode bindApplication 54.46 ms 19.24

    ms Release ResourcesManager#getRes ources 4.62 ms 3.97 ms Release ResourcesImpl#updateCon figuration 3.98 ms 2.62 ms Release LoadApkAssets 1.3 ms 0.68 ms Release VectorDrawable#inflate 0.41 ms 0.39 ms Release
  39. Rules

  40. Rules can hurt you - keep class com.mypackage.** - keep

    com.somepackage.R$* - keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
  41. Case Study keepclassmembers class **.R$* { public static <fields>; }

  42. Transitive R classes App Module1 Module2 Module3 Module4 Module5 aar

    aar aar aar
  43. Transitive R classes App Module1 Module2 Module3 Module4 Module5 aar

    aar aar aar R.class R.class R.class R.class R.class R.class R.class R.class R.class
  44. 11.8 mb 596.6 ms 6.4 mb (45%) 361.3 ms (39%)

    Non Minified Minified
  45. 6.4 mb 361.3 ms Minified 6.6 mb (+3%) 487.1 ms

    (+34%) Minified with faulty rules
  46. 6.4 mb 361.3 ms Minified 6.6 mb (+3%) 487.1 ms

    (+34%) Minified with faulty rules Dex Count: 1 Dex Size: 634KB Dex load: 0.07ms Dex Count: 2 Dex Size: 894KB Dex load: 0.09ms
  47. Rules • Be aggressive instead of defensive when defining proguard

    rules. • Avoid wildcard package rules. • Use @Keep to attach rules to codebase instead of rules files. • Scan for rules which third party rules are importing in your codebase.
  48. Imported Rules proguard-rules.pro -printconfiguration

  49. Imported Rules proguard-rules.pro -printconfiguration build/outputs/mapping/release/configuration.txt

  50. Further Optimisations proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile(‘proguard-android-optimize.txt’), 'proguard-rules.pro'

  51. Further Optimisations gradle.properties android.enableR8.fullMode=true

  52. Debugging Rules -addconfigurationdebugging

  53. Proguard Playground https://playground.proguard.com/

  54. None
  55. Further Reading • https://developer.android.com/studio/build/shrink-code • https://www.zacsweers.dev/android-proguard-rules/ • https://jakewharton.com/blog (Search for

    R8 optimization)
  56. Thanks @_jitinsharma