UnityのAndroid開発における64K問題について

こんにちは。[CYBIRDエンジニア Advent Calendar](http://qiita.com/advent-calendar/2016/cybird)、20日目のyyama2です。

19日目は@cy-katsuhiro-miuraさんの「[AWS IoT/MQTTで３Dをグリグリ動かそうとした話](http://qiita.com/cy-katsuhiro-miura/items/c80c00444712291e0213)」でした。

とてもわかり易い~~一部の発言の責任を僕に押し付けている非常に悪意のある~~記事でしたね！
ニフティクラウドは僕もとても気になっているサービスではあるので、是非触ってみたいと思いました。

## さて、本題

ちょっと前に発生した、64K問題についていろいろとまとめて見ました。


## 64K問題ってなに？
Android のアプリパッケージ(apk) はデフォルトでメンバとかメソッドとか、いろんな参照を 65535(64K) までしか定義することができないらしいのです。
それを超えてしまうとapkのビルドに失敗してしまいます。
↓こんなエラー
```
trouble writing output: Too many field references: 67790; max is 65536.
```
これを通称 __64K問題__ と呼んでいます。

## 関数が増えてしまう理由
64Kもの数のメソッド数になってしまった大きな原因があります。
それは、

- __外部ライブラリの参照__

です。

ライブラリは、様々な機能があらかじめパッケージングされて、 .jar や .aar のような形式で圧縮されており、そのファイルをプロジェクトに追加するだけで、簡単にその機能を呼び出すことができます。
ライブラリには、様々な機能が搭載されているのですが、その分アプリでは使用しない機能も搭載されていることが多くあります。ライブラリを多く参照するとそういった無駄な機能が積み重なり、参照数が増えてしまいます。


## ライブラリ
Androidで参照がカウントされてしまうライブラリ形式は下記の2点です。

- .jarファイル
- .aarファイル(Androidライブラリ)

### .jarファイル
.jar(Java ARchive)ファイルはjavaで作成されたプログラムを圧縮したファイルです。.jar ファイルに含まれる参照数はそのままAndroidアプリの参照数に加算されます。

### .aarファイル(Androidライブラリ)
.aar(Android ARchive)ファイルは、Androidプロジェクトを圧縮したもので、Androidのライブラリプロジェクトと同じ役割です。ライブラリプロジェクトは、リソースファイル(xmlや画像など)のみでのプロジェクト化も可能で、javaファイルを含まないライブラリも存在します。ただ、ライブラリプロジェクトを参照すると、メソッド数が 2228 増加してしまします。(具体的な原因は調査しきれていませんが、R.javaとか自動生成系のクラスが悪さをしているのではないかと思います。)
__※これは担当アプリで発生した現象で、簡易で検証用に作成したアプリでは発生しなかったので別の原因かもしれません。__

## SDK
担当したサードパーティ製（ここでは、Unity製（C#）以外を指します。）のSDKを多く使用していました。

- カスタマーサポート系
- 広告系
- 分析系
- アプリ内課金系
- チャット系
- Google Play Games for Unity
- SNS連携
- 社内ライブラリ

など、サードパーティのSDKがそれぞれ別々の通信ライブラリを使用して通信をしたり、 ローカルにデータを保存していたり、Androidのネイティブ機能にアクセスしたりしています。
それぞれのどれもアプリの運営に必要な機能です。これらのSDKから参照数をいかにして削れればいいでしょうか。

## 考えられる解決方法

### Multi-dexサポートビルド
Dexを分割して起動出来るようにする機能。
この問題が起こったときの__解決方法序列第1位__です。
Androidが公式に公開している対応方法ですが、幾つかの制限があります。
#### 残念な制限１　Android 5.0(API 21)以降のサポート
残念なことに、Android5.0以降のサポートになります。
今のソーシャルゲームで5.0以降のみ対応！なんてのは少ないのでは無いかな。。。

#### 残念な制限２　Unityがサポートしていない。
そもそも、Unityがサポートしておらず、中の方々も2012以降発言していません。（多分）
こんな問題解決するくらいなら、機能のバージョンアップの方が嬉しいもんね！

## 実際に行った対応
と言うわけで、対応する方針として上がった方針は下記になります。

- 削減できるライブラリの抽出
- ライブラリの改修

まず各SDKが参照しているライブラリを抽出し、削除できそうなものを調べます。
調査した結果、
GooglePlayGamesのSDKのみ、不要なライブラリを参照していることがわかりました。

### Google Play Games

#### Google Play Games for Unity
GooglePlayGamesの利用には、Google製のUnityプラグインを使用して実装していました。このSDKが、play-services-nearby というライブラリを参照しており、このライブラリを削減することでメソッド数の削減ができると考えました。
ただ、このSDKはライブラリ内でnearbyの初期化を行っており、nearbyのライブラリをプロジェクトから除いてしまうと参照できなくなり、例外が発生してしまうようになりました。
プラグイン自体の修正を行っても良かったのですが、
サードパーティ製のライブラリの修正→動作保証が・・・

なので↓の対応・・・

#### GooglePlayGames 用のライブラリの実装
Google製のプラグインでは、不要なライブラリの削減が出来ないので、GooglePlayGamesのライブラリの作成を行うことで、play-services-nearbyの実装を削減を行いました。

この段階で、メソッド数は35672だった記憶があります。（正確でなくてすみません。。。）
あと少しです。そこで次の対応も行いました。

### 社内ライブラリ
#### アンドロイドプロジェクトを統合
アンドロイドプロジェクトをUnityに組み込む際には、AndroidProjectで .aar を出力してUnityプロジェクトにインポートします。
これだと、 .aar ファイルが2つで、2228 × 2 ものメソッド数が追加でカウントされてしまいます。なので、その参照を 2228 × 1 にするために、ライブラリの統合を行うことにしました。
社内ライブラリは .aar ではなくライブラリプロジェクトで提供されているということもあり、別々のプロジェクトとしてAndroidStudioにインポートされている二つのプロジェクトを統合することは容易でした。

#### ついでにGooglePlayGamesのプラグインも統合
GooglePlayGamesのプラグインを自作したことで、このプラグインの統合も図ることができるようになりました。社内ライブラリの統合と同じように、このライブラリも統合し実装しました。

### とりあえず解決！
こんな感じで、参照数の削減をして問題の解決をしましたとさ！


## この件で考えたこと
### ライブラリの削減
今後はサードパーティのSDKは使用しない方向で企画時に考えるべきだと思います。
前述したライブラリのうち、下記はUnityがサポートします。

- 広告 → UnityAds
- 分析 → UnityAnalytics
- アプリ内課金 → UnityInAppPurchase

ただ、広告と分析については外部サービスの方が圧倒的に優秀なので、なんとも言えません。。。

### 自分で頑張る！
Androidネイティブの機能を利用しようとすると、どうしても外部ライブラリに頼ってしまいたくなると思いますが、外部ライブラリは無駄な機能も多い！
なので、極力自分で作成した方が吉！


## おわりに
いかがだったでしょうか？
根本的な解決にはなっていない対処でしたが、
これが業務っぽいですよね！
いろいろなことが絡み合って。本当の解決策が取れない。

明日は3日目の[Chefを利用して運用者の作業を増やす５つの方法](http://qiita.com/gotyoooo/items/4028e8ec6c654b93d800)の@gotyooooさんの再登場です！
面白い記事が書かれること必至なので乞うご期待！！