Realm導入によるAPK肥大化を防ぐ(Split APK)

[Twitterクライアントの内部DBをSQLiteからRealmに移行したときのノウハウまとめ - Qiita](http://qiita.com/takke/items/a35f278279ce750e4d79) 


Realm公式の解説通りに下記のように build.gradle に記述して Realm を導入すると APK サイズが数MBほど大きくなります。

```groovy:build.gradle
compile 'io.realm:realm-android:0.82.0'
```

これは Realm の Core 部分が NDK で書かれており、全アーキテクチャ用の .so ファイルが含まれているためです。

そこでアーキテクチャ別に APK を用意する(分割する)ことで APK サイズの増加量を 700 KB 程度まで抑えることができました。

公式の情報は [Reducing your Android APK size when using native libraries — how we made Realm 76% smaller in your apps - Realm is a mobile database: a replacement for SQLite & Core Data](https://realm.io/news/reducing-apk-size-native-libraries/) です。

> Our distribution package (available on our website under Download->Java) contains a folder called ‘eclipse’. This folder contains a split version of the Realm library. All you need to do is to copy the small jar file into the libs folder of your app and copy the four folders in the src/main/jniLibs directory.


# ZIP版をダウンロードし、配置する

gradleにcompileの1行で導入するのはお手軽なんですが、残念ながら全アーキテクチャの .so ファイルを含む jar が利用されるため APK が肥大化してしまいます。

そこで ZIP 版を展開してアーキテクチャ毎の .so ファイルを配置します。

- [公式サイト](https://realm.io/) のトップにあるリンクから ZIP 版をダウンロードします。
- ZIP を展開し、eclipse フォルダにある realm-0.82.0.jar をアプリの libs フォルダに、各アーキテクチャのフォルダを src/main/jniLibs ディレクトリにコピーします。

![Gyazo - a81c942d2fff3e56fd3fea4bec55ec31.png](http://i.gyazo.com/a81c942d2fff3e56fd3fea4bec55ec31.png)

![Gyazo - 242c886314f7d7f11bc2dec81727f34f.png](http://i.gyazo.com/242c886314f7d7f11bc2dec81727f34f.png)

# APK分割設定を行う

公式に書いてあるとおり、下記のように記述します。

```groovy:build.gradle
android {
    // Some other configuration here...

    splits {
        abi {
            enable true
            reset()
            include 'x86', 'armeabi', 'armeabi-v7a'
            universalApk false
        }
    }
}
```

上記を記述後に "Sync Project with Gradle Files" を実行すると assembleFreeArmabiRelease 等のタスクができます。

![Gyazo - aef68fd47207bf63ff7a97f614b4f3a2.png](http://i.gyazo.com/aef68fd47207bf63ff7a97f614b4f3a2.png)


# アーキテクチャ毎にバージョンコードを変更する

以降はPlayストアで公開するためのお作法です。

Playストアでアーキテクチャ別の複数のAPKを登録する場合、各アーキテクチャのAPKが異なるバージョンコードを持つ必要があります。そこで、下記のように記述してバージョンコードを自動設定されるようにしました。

```groovy:build.gradle
ext {
    versionCode = 458
    versionName = '8.1.2'

    // map for the version code
    versionCodes = ['armeabi':0, x86:1]
}
```

```groovy:app/build.gradle
    android.applicationVariants.all { variant ->
        // assign different version code for each output
        variant.outputs.each { output ->
            def arch = output.getFilter(com.android.build.OutputFile.ABI)
            output.versionCodeOverride = android.defaultConfig.versionCode * 10 + rootProject.ext.versionCodes.get(arch)
        }
    }
```

こうして作成した APK をストアのアドバンスドモードのほうで登録すると下記のようになりました。

<blockquote class="twitter-tweet" lang="en"><p lang="ja" dir="ltr">ほうほう、APK分割するとストアではこう登録するのか。 <a href="http://t.co/flSytm49vl">pic.twitter.com/flSytm49vl</a></p>&mdash; 竹内裕昭 (@takke) <a href="https://twitter.com/takke/status/628044373725294592">August 3, 2015</a></blockquote>


# APKファイル名の変更

各アーキテクチャ毎にAPKファイル名を変更するために私は下記のようにしています。

```groovy:build.gradle
    applicationVariants.all { variant ->
        if (variant.buildType.name.equals("release")) {

            variant.outputs.each { output ->
                if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                    System.println("* output filename update : [${variant.buildType.name}]" +
                            "[${variant.productFlavors[0].name}]" +
                            "[${variant.productFlavors[0].applicationId}]"
                    )

                    def file = output.outputFile
                    def d = new java.text.SimpleDateFormat("yyyyMMdd_HHmm").format(new Date())
                    def flavor = variant.productFlavors[0]
                    def shortVersionName = defaultConfig.versionName.replaceAll('\\.', '')
                    def arch = output.getFilter(com.android.build.OutputFile.ABI)
                    def newName = "TwitPane_${flavor.name}_${shortVersionName}_${d}_${arch}.apk"
                    output.outputFile = new File(file.parent, newName)
                }
            }
        }
    }
```


# 参考
- [Android - Twitterクライアントの内部DBをSQLiteからRealmに移行したときのノウハウまとめ - Qiita](http://qiita.com/takke/items/a35f278279ce750e4d79) 
- [Reducing your Android APK size when using native libraries — how we made Realm 76% smaller in your apps - Realm is a mobile database: a replacement for SQLite & Core Data](https://realm.io/news/reducing-apk-size-native-libraries/)
- [Apk Splits - Android Tools Project Site](http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits)