to ∞ (~65k) and beyond! - sebastiano gottardo - codemotion milan 2016

62
To ∞ (~65K) and beyond! Sebastiano Gottardo, Android Engineer +SebastianoGottardo - @rotxed 1

Upload: codemotion

Post on 07-Jan-2017

56 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

To ∞ (~65K) and beyond!Sebastiano Gottardo, Android Engineer +SebastianoGottardo - @rotxed

1

Page 2: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Context

2

Page 3: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Context

• OSS, community, libraries, frameworksThe Android open-source community is enormous, and brings to the table thousands of libraries and frameworks.

• ✓ Faster development, contributions and improvements These tools allows you to build better apps quicker. In addition, contributions by other users lead to improvements.

• ✗ Using too many can lead to heavy APK, long loading times, bugs There are tradeoffs.

3

Page 4: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

4

65536

Page 5: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Dalvik Executable (DEX)

• Container of the compiled source code .java files are compiled into .class files, then packed into a .dex file

5

Page 6: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Where is the limit

• Not runtime, not DEX, but Dalvik’s instruction set!

6

https://goo.gl/ajlxwX

Page 7: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Crossing the limit

• Unable to build an APK

• Panic

• Console output

7

Older build system Newer build system

Page 8: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Is this going to change?

8

Page 9: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Context

• No(t likely)Lose retro-compatibility!

• ART (Android Runtime)The new runtime used starting with Lollipop provides a way around.

• What to do then?There are different solutions/approaches.

9

Page 10: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Approaches

• MultiDexGoogle’s own solution.

• Secondary DEX Get your hands dirty.

• Shrinking / Reasoning ProGuard is your friend, and so is your brain.

10

Page 11: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex

11

Page 12: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex

• Google’s way around this limitation aka what’s in the docs

• Still not the “recommended” approach More on that later on.

• Different working mechanisms depending on platformDalvik ≠ ART.

12

Page 13: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex: Dalvik VS ART

13

ARTDalvik

Page 14: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex with ART

• App’s source code is compiled and packed into multiple DEX files Ultimately packed in the APK

• At install time, the OS compiles the code of the DEX files into a single OAT file (ELF)AOT compilation instead of JIT compilation

14

Page 15: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex: Dalvik VS ART

15

ARTDalvik

Page 16: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex with Dalvik

• App’s source code is compiled and packed into a single DEX fileUltimately packed in the APK

• Dalvik is unaware of DEX files other than the main oneclasses.dex

• Multiple DEX files must be loaded at runtimeCan be troublesome (a.k.a. sucks)

16

Page 17: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MultiDex - Setup

17

Page 18: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Setup 1/3

1. Gradle-wise setup

18

android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { ... minSdkVersion 15 targetSdkVersion 23 ... // Enabling multidex support. multiDexEnabled true } ...}

Page 19: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Setup 2/3

2. Include the MultiDex support library

19

dependencies { compile 'com.android.support:multidex:1.0.0'}

Page 20: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Setup 3/3

3. Enable MultiDex in your code

20

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.multidex.myapplication"> <application ... android:name="android.support.multidex.MultiDexApplication"> ... </application></manifest>

a

public class YourAwesomeApplication extends MultiDexApplication { ...

}b

@Overrideprotected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }

c

Page 21: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Advantages

• Well integrated with the build chainNo magic involved.

• Seamless support across platform versionsBeing a support library. One code, one love!

• Extremely easy to enableLiterally takes 3 minutes.

21

Page 22: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Disadvantages

• Limited in choosing what goes whereEsp. true for libraries that sport native components.

• MultiDex on Dalvik is unreliableFor 14 < API < 21 crashes may occur.

• Secondary DEX files are loaded at runtimeStartup time is increased! (unless you defer installation)

• Build times increaseVariants may help (dev — min 21, speeds things up)

22

Page 23: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Secondary DEX

23

Page 24: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Secondary DEX

• Historically, the first workaroundA blog post by Fred Chung, a Googler at the time (2007)

• Relied on customizing the build chain Some Ant magic

• Used an interface-based approach

24

Page 25: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

How does it work

• Put one or more libraries into a separate DEX file Limited to .jar files, .aar not supported

• At runtime, load the secondary DEX on a custom ClassLoaderDifferent from the default ClassLoader!

• Invoke the libraries’ methodsInterface, Reflection

25

Page 26: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Quick example w/ Reflection

26

twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(TWITTER_APP_KEY, TWITTER_APP_SECRET); twitter.setOAuthAccessToken(new AccessToken(token, tokenSecret));

Page 27: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Quick example w/ Reflection

27

...twitter = twitterManager.initializeTwitter(TWITTER_APP_KEY, TWITTER_APP_SECRET, token, tokenSecret); ...

public Object initializeTwitter(String TWITTER_APP_KEY, String TWITTER_APP_SECRET, String token, String tokenSecret) { try { Object twitterFactoryInstance = getClassCached(twitterFactoryFQN).newInstance(); Method getInstanceMethod = getMethodCached(twitterFactoryFQN, "getInstance", null); Object twitterInstance = getInstanceMethod.invoke(twitterFactoryInstance, null); Method setOAuthConsumerMethod = getMethodCached(twitterFQN, "setOAuthConsumer", String.class, String.class); setOAuthConsumerMethod.invoke(twitterInstance, TWITTER_APP_KEY, TWITTER_APP_SECRET); Class atClass = getClassCached(accessTokenFQN); Object atInstance = atClass.getDeclaredConstructor(new Class[] {String.class, String.class}).newInstance(token, tokenSecret); Method setOAuthAccessTokenMethod = getMethodCached(twitterFQN, "setOAuthAccessToken", atClass); setOAuthAccessTokenMethod.invoke(twitterInstance, atInstance); return twitterInstance; } catch (Exception e) { e.printStackTrace(); } return null;}

Page 28: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Advantages

• Choose exactly what goes whereTake that, MultiDex

• Choose exactly when to load secondary DEXSo not to impact cold start times

28

Page 29: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Disadvantages

• Quite hard to setupCopy DEX to the appropriate dir, create ClassLoader instance

• Quite hard to work withEsp. if you rely on Reflection

• Versioning! How to handle updates?

29

Page 30: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Improvements

• Same approach as Google’s MultiDexPatches the Class Loader so to look for other DEX files

• Specify the dependencies as ‘provided’So to be resolved at compile/build time, stripped in the final APK

• Best of two worlds (kind of)Removes the need for ifaces, Reflection, still a bit cumbersome

30

Page 31: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Shrinking / Reasoning

31

Page 32: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

— Google MultiDex online documentation

Before configuring your app to enable use of 65K or more method references, you should take steps to reduce the total number of references called by your app code, including methods defined by your app code or included libraries.

32

Page 33: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ProGuard in pills

• Shrink, optimize, obfuscate

• Works both for your code and for third-party libraries

• Has an impact on APK size, methods number, performance and security

33

Page 34: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ProGuard ProTips

• Get familiar with writing appropriate rulesThe stricter, the better

34

-keep class !android.support.v7.internal.view.menu.**,android.support.** {*;}

• What’s wrong with the following keep rule?

-keep class com.google.** { *; }

-keepnames class com.google.** { *; }

-keepclassmembernames class * { @com.google.android.gms.common.annotation.KeepName *; }

• Adopts an exclusion-based approach

Page 35: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ProGuard ProTips

35

-keep class !android.support.v7.internal.view.menu.**,android.support.** {*;}

-keep class com.google.** { *; }

-keepnames class com.google.** { *; }

-keepclassmembernames class * { @com.google.android.gms.common.annotation.KeepName *; }

• Adopts an exclusion-based approach

• Get familiar with writing appropriate rulesThe stricter, the better

• What’s wrong with the following keep rule?

Page 36: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ProGuard: default rules

36

android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }

• ProGuard already includes common rules for common cases~/sdk/tools/proguard/lib/proguard-android.txt

-keepclasseswithmembernames class * { native <methods>; }

Page 37: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

DexGuard: beefier, for beefier companies

37

• ProGuard’s bigger (and pricier) brother

• Advanced features: res/strings/.so encryption, stronger obfuscation,tamper detection, its own multidex! and more

• Pluggable encryption! I mean, seriously, that exclamation mark has a reason

• Default rules are much more extensive

Page 38: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ProGuard and third-party libs

38

• Many provide the ProGuard statement (a.k.a RTFM)e.g., Square, Facebook, …

• Pay attention to ‘keep-all’ rules!especially for libraries with many methods

Page 39: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

… wait, how do I count libraries methods?

39

… how do I count methods at all?!

Page 40: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

inloop’s “APK method count”

40

Page 41: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

inloop’s “APK method count”

41

Page 42: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

inloop’s “APK method count”

• Simple website with drag-and-drop interface

• Drop the APK and see the (locally) computed countNo APKs uploaded :+1:

• http://inloop.github.io/apk-method-count/

42

✓ Easy, immediate, private✗ Build every time

Page 43: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ClassyShark

• Useful Java tool to inspect APKsWritten by Boris Farber (Googler)

• Inspect number of classes, members, methods, dependencies, …Acts as a simple decompiler, too

43

Page 44: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ClassyShark

44

Page 45: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ClassyShark

45

Page 46: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

ClassyShark

• Useful Java tool to inspect APKsWritten by Boris Farber (Googler)

• Inspect number of classes, members, methods, dependencies, …Acts as a simple decompiler, too

46

✓ Easy, plenty of info, multi-purpose✗ Build every time, more oriented to agnostic debugging

Page 47: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

dexcount-gradle-plugin

• Gradle plugin, developed by KeepSafe

• See the current methods count every time you buildJust open the Gradle console

• Outputs per-package information in a ${variant}.txt file

47

Page 48: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

dexcount-gradle-plugin

48

Page 49: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

dexcount-gradle-plugin

49

Page 50: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

dexcount-gradle-plugin

• Gradle plugin, developed by KeepSafe

• See the current methods count every time you buildJust open the Gradle console

• Outputs per-package information in a ${variant}.txt file

50

✓ Keep the count controlled at a glance✗ Cumbersome to see single deps count

Page 51: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

• Developed by Sebastiano Gottardo, Nicola Miotto, Dario Marcato

• Allows to see methods and dependencies for libraries beforehand

• Compare different libraries in terms of “lightweight-ness”

51

Page 52: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

52

• More than 20000 libraries and dependenciesWhat’s missing is calculated upon the first request

• For each entry

1. dex/jar size

2. direct count + dependencies count

3. multiple versions (monitor changes over time) + charts

Page 53: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

53

Page 54: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

54

Page 55: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

55

Page 56: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount - AS plugin

56

• Parses `build.gradle` and directly shows the count

• Convenient when you drop a dependency right away

Page 57: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount - AS plugin

57

• Parses `build.gradle` and directly shows the count

• Convenient when you drop a dependency right away

Page 58: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

MethodsCount

58

✓ See the count before having to hit the limit✓ Compare different libraries Worth having 4000 methods instead of 900?✓ Analyze dependencies No, Guava is not a good excuse to have 15000+ methods✓ Integrated in AS, thanks to the plugin

✗ The count is before ProGuard → Upper bound!

Page 59: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Bottom line

59

Page 60: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Bottom line

• The limit is here, at least for some timeLearn to deal with it

• The recommended way is “Do NOT cross the limit!”And use ProGuard. Your nerves will thank you later!

• If you do need to cross, there are solutions (with drawbacks)MultiDex, secondary DEX

60

Page 61: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Routine

1. PM shows interest in a new library:eyes_roll:

2. With dex-gradle-plugin, check the current count

3. Check MethodsCount to see how many methods, size and deps

4. Evaluate whether fits a secondary DEX

5. Consider alternatives (again, MethodsCount)

61

Page 62: To ∞ (~65K) and beyond! - Sebastiano Gottardo - Codemotion Milan 2016

Useful resources

• Timothy Mellor: https://github.com/tmelz/multidex_notes

• Jeroen Mols: http://jeroenmols.com/blog/2016/05/06/methodcount/

62

@rotxed+SebastianoGottardo

Follow me