본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] MultiDex 에 대한 이야기

by 돼지왕왕돼지 2020. 8. 20.


android multidex



https://developers.soundcloud.com/blog/congratulations-you-have-a-lot-of-code-remedying-androids-method-limit-part-1

https://developer.android.com/studio/build/multidex.html


-

DEX file 의 method index 는 16bit 로 제한되어 있다.

이는 2^16 인 65,536 갯수 이상의 method reference 를 가질 수 없다는 것이다.

이 숫자에는 android framework methods, library methods, 그리고 너의 코드에 있는 methods 를 포함한 것이다.


64K 가 많아보이지만, android support lib 을 비롯하여 3rd party lib 이 들어가는 순간 쉽게 over 할 수 있다.

자주 쓰이는 Guava 나 Jackson 같은 녀석들은 특히 많은 method 를 가진 lib 으로 유명하다.



-

method limit 으로 빌드를 할 수 없다면 다음 형태의 에러가 발생한다.

trouble writing output:

Too many field references: 131000; max is 65536.

You may try using --multi-dex option.


Conversion to Dalvik format failed:

Unable to execute dex: method ID not in [0, 0xffff]: 65536



-

jumboMode 라는 녀석이 있고, 사람들은 이 녀석을 조작하면 해결될 것이라 하는데.. 이 녀석과 이 method limit 은 상관 없다고 한다.



-

MultiDex 를 피하는 방법들은 다음과 같다. ( 돼왕 : 1번 이외에는 사실 권장되지 않는 방법이라 생각한다. )


1. Proguard 를 사용하여 unused function 등을 제거한다.

2. 3rd patry lib 을 사용한다면 & 해당 버전에 대한 분석 등이 끝났다면, 사용하지 않는 녀석들을 trim 해서 새로운 lib 을 만들어 넣는다. (분석, 유지보수 등등이 쉽지 않다.)

3. 3rd paty lib 을 우리 코드로 import 시킨다. 다시 말하면, 필요한 부분만 발췌해서 가져오고 해당 3rd party lib 을 사용하지 않는다.

4. method 들을 native 단으로 이동한다. (JNI, so)



-

MultiDex 란 code 를 잘라서 여러개의 DEX 에 넣는 것을 의미한다.

ART 에서는 결국 여러개의 DEX 파일을 하나의 ELF(executable and linkable format) binary 로 만들기 때문에 괜찮다.

하지만 Dalvik 을 사용하는 LOS 이전 버전에서는 tool chain 이 몇가지 일을 더 수행해야 한다.


1. MultiDex support library 를 포함시켜야 하고, application 이 MultiDexApplication 을 상속하도록 하거나 MultiDex loader 를 직접 invoke 시켜줘야 한다.

2. Build time 이 증가한다. 

3. preDexLibraries flag 가 작동하지 않는다. 이 말은 코드 한 줄만 바꾸어도 lib dependency 를 포함해서 re-dex 를 다시해야 하기 때문이다. ART 는 이 제약이 없다. 왜냐하면 모든 DEX file 이 하나의 OAT(Of Ahead Time) file 로 compile 되기 때문이다.

4. Dagger 같은 full class graph 가 필요한 lib 을 사용할 경우, 추가 DEX 파일이 object graph 를 만들기 전에 로드가 완료되어 있어야 한다. 이 말은 app constructor 에서 object graph 를 만들 수 없다는 얘기이다. 대신 attachBaseContext 를 override 해서 여기서 super 를 부른 후 만들어야 한다.



-

MultiDex 를 사용할 때 주의사항들이 있다. 특히 LOS 이전 버전에서..(https://developer.android.com/studio/build/multidex.html#limitations)

    두번째 DEX 파일의 크기가 크다면, ANR 이 발생할 수 있다. -> Proguard 를 이용해서 code shrinking 을 해라.

    5.0(API 21, LOS) 이전 버전에서 multidex 를 사용한다면 framework 의 linearalloc limit(복잡한 interface 사용시 발생) 때문에 주의를 기울여야 한다. (https://issuetracker.google.com/issues/37008143)

    ICS 이전 버전에서는 DEX index limit 전에 linearalloc limit 을 먼저 마딱뜨리기 쉽다. 따라서 그 이전버전에서는 startup 이나 특정 class load 시 죽지 않는지 철저히 테스트해야 한다.

    linearalloc limit은 ICS 에서 증가되었지만, 완벽히 문제가 해결된 것은 아니라, LOS 이전 버전도 마찬가지로 테스트가 잘 되어야 한다.



-

build performance 보완을 위해서 minSdkLevel 21 을 가진 flavor 를 만들어 사용하는 것도 idea 중 하나. (단! 이 때 min sdk level api 등의 lint 가 제대로 작동하지 않을테니 이런 것은 주의해야 한다.)

이렇게 하면 incremental build 의 성능이 좋아진다. (적은 change에 대한 새로운 빌드)




-

android 5.0 (LOS) 미만에서는 아래와 같은 설정을 통해 multidex 를 만들 수 있다.

// build.gradle

dependencies {

  compile 'com.android.support:multidex:1.0.1'

}


android{

    defaultConfig{

        ..

        multiDexEnabled  true

    }

}


Application 이 없었다면, manifest 에 바로 MultiDexApplication 을 바로 세팅해주면 된다.

 <application

            android:name="android.support.multidex.MultiDexApplication" >

        …


Application 을 사용하고 있었다면 MultiDexApplication 을 상속하도록 고쳐야 한다.


만약 MultiDexApplication 을 상속하도록 하는 것도 쉽지 않다면.. attachBaseContext() 를 override 해서 super 이후에 MultiDex.install(this) 를 호출해주어야 한다.

public class MyApplication extends SomeOtherApplication {
  @Override
  protected void attachBaseContext(Context base) {
     super.attachBaseContext(base);
     MultiDex.install(this);
  }

  ...
}


MultiDex.install 을 호출하기 전에 reflection 이나 JNI 콜을 하지 않도록 주의해야 한다.

만약 이를 어기면 ClassNotFoundException 나 VerifyError 가 발생한다. ( 다른 dex 파일에 대한 로드가 끝나지 않아서 )


Multidex 가 되면 classes.dex, classes2.dex, classes3.dex 등이 생성된다.

그리고 runtime 에는 특별한 class loader 를 사용해서 DEX files 에 있는 method 를 찾아 수행한다.



-

android 5.0 이상에서는 ART 를 사용한다.

ART 는 기본적으로 multi dex file load 를 제공한다.

ART 가 앱 설치 타임에 classesN.dex 파일들을 찾아서 하나의 .oat 파일로 만들고, 이 녀석으로 수행한다.

그래서 minSdkVersion 21 이상에서는 multidex support lib 을 포함할 필요가 없다.

대신 android.defaultConfig 에 multiDexEnabled 를 true 는 주면 모두 끝이다.



-

Multidex 를 만들 때 build tool 은 어떤 class 를 primary dex 에 넣어야 하는지 복잡한 절차를 걸쳐 결정한다.

startup 때 필요한 class 가 primary DEX 에 없다면 앱은 NoClassDefFoundError 를 뱉으며 죽을 것이다.


이 경우 primary DEX 에 꼭 들어가야 하는 것을 build type 에 multiDexKeepFile 이나 multiDexKeepProguard 로 정의해주어야 한다.


ex1)

// multidex-config.txt

com/example/MyClass.class

com/example/MyOtherClass.class


// build.gradle

android {

    buildTypes {

        release {

            multiDexKeepFile file('multidex-config.txt')

            ...

        }

    }

}



ex2)

// multidex-config.pro

-keep class com.example.MyClass

-keep class com.example.MyClassToo

// -keep class com.example.** { *; } 로 해당 package 모두를 명시할 수도 있다.


// build.gradle

android {

    buildTypes {

        release {

            multiDexKeepProguard file('multidex-config.pro')

            ...

        }

    }

}



-

multidex 를 사용하면, 어떤 class 를 첫번쨰 dex 에 넣고 어떤 class 를 다른 dex 에 넣을지 결정하는 복잡한 로직을 타면서 build time 이 많이 증가한다.

ART 에서는 (LOS, API 21 이상에서 사용 가능) pre-dexing 이 가능하다.

Android Studio 2.3 이상을 사용한다면 IDE 가 기본으로 LOS 이상 단말에 배포할 때 자동으로 해준다.



-

끝!!






댓글0