NCMB Unity SDKがAdMob等のAndroidプラグインと競合する場合の解決

# 情報アップデート

*2017/10/6追記*
v3.1.0から同梱のライブラリがjarからaarになり、「パターン２」の問題がやや解消されました（フォルダ違いによるダブりは可能性としてあり）

# はじめに
ニフティクラウド mobile backend(以降、NCMB)はiOS/Androidへ簡単にネットワーク機能が導入できるサービスSDKです。
http://mb.cloud.nifty.com/

そのUnityプラグインがこちら。

~~NCMB Unity Plugin v3.0.0~~
NCMB Unity Plugin v3.1.0
https://github.com/NIFCloud-mbaas/ncmb_unity/releases

このパッケージには、なるべく簡単に導入してもらうために、はじめからPlugins/Android/下に AndroidManifest.xmlファイルが同梱されています。
なのですが、本来AndroidManifestファイルは使用する機能に合わせて(可能であれば)開発者自身が書くもので、SDKに決め打ちのAndroidManifestファイルが入っていると他のSDKとバッティングすることがあります。

また、NCMB Unity Pluginには動作に必要なライブラリが~~２~~８つ同梱されています。
~~Android Support Library (android-support-v4.jar)~~
~~Google Play Services Library (google-play-services.jar)~~

* play-services-base-11.0.0.aar
* play-services-basement-11.0.0.aar
* play-services-gcm-11.0.0.aar
* play-services-iid-11.0.0.aar
* play-services-tasks-11.0.0.aar 
* support-compat-26.0.2.aar
* support-core-utils-26.0.2.aar
* support-v4-26.0.2.aar

同様に他のSDKを導入したときに、これらのライブラリもまた競合することがあります。
本稿では、よく併用されているGoogle Mobile Ads Pluginを例にとり、競合の解決について紹介します。

Google Mobile Ads Unity Plugin v3.5.0
https://github.com/googleads/googleads-mobile-unity/releases

# パターン１：Android Manifestファイルの設定が食い違う

Unityはプロジェクトファイル内に複数のAndroidManifest.xmlがあったとき、それらをマージしようとします。
何も考えずにNCMB, Google Mobile Adsのパッケージをプロジェクトにぶっこんでビルドしようとすると次のエラーが表示されます。

![大事なことなので二回言った感](https://qiita-image-store.s3.amazonaws.com/0/19010/506fbaf2-3bb4-fff3-ac00-3b6ad29253d2.png)

```
Unable to merge android manifests. See the Console for more details. See the Console for details.
```

コンソールに表示されるエラーログを見ていきます。

```
Error: [Temp\StagingArea\AndroidManifest-main.xml:3, 
C:\ [path] \Temp\StagingArea\android-libraries\GoogleMobileAdsPlugin\AndroidManifest.xml:2] 
Main manifest has <uses-sdk android:minSdkVersion='10'> but library uses minSdkVersion='14'
```

NCMB Unity SDKのパッケージをインポートすると、Plugins/Androidフォルダ直下にAndroidManifest.xmlが配置されます。
Unityはビルト時にAndroid直下にあるマニフェストファイルを「Main manifest」だと認識します。
GoogleMobileAdsPluginはAndroid APIバージョンの下限14(Android 4.0)なのに、Main Manifestファイルは10(Android 2.3.3)って書いてあるぞオメー、と怒られてしまう塩梅です。

そこで、NCMB由来のAndroidManifest.xmlの中で

```
	 <uses-sdk android:minSdkVersion="10"/>
```

となっている箇所を

```
	 <uses-sdk android:minSdkVersion="14"/>
```

と書き換えましょう。

#パターン２：Android用のライブラリがダブる

![Build failure](https://qiita-image-store.s3.amazonaws.com/0/19010/9bbdc9c5-b792-c679-0754-69a1db489fa8.png)

```
Unable to convert classes into dex format. See the Console for Details
```

こちらのエラーログは長いので省略しますが、dex formatの変換に失敗しているとき、Plugins/Androidの中に同種のライブラリが含まれている可能性があります。

例の場合、NCMB Unity SDKに含まれている「~~android-support-v4.jar~~ support-v4-26.0.2.aar」とGoogle Mobile Ads Unity Pluginに含まれている「support-v4-24.0.0.aar」は~~拡張子は違えど~~同じライブラリなので、~~android-support-v4.jar~~ バージョンが古い方を削除します。

~~同様に、「google-play-services.jar」(の中に含まれているクラス)と、Google Mobile Ads Unity Pluginがインストールする「play-services-ads-10.2.6.aar」等いくつのライブラリが競合しているので、google-play-services.jarの方を削除します。~~

同様に、「play-services-YYY-XX.X.X.aar」でYYYのライブラリ名が同じ奴はダブりなので、古いほうを消します。

~~ただし、NCMBが使用するGoogle Cloud Messaging関連のライブラリは必要なので、これだけ個別に入手して配置します。
手順は二通りあります。~~

# NCMB SDK 3.0.1以前を使う場合の対処

もし何らかの理由でNCMB SDK 3.0.1以前を利用する場合は、同梱の「android-support-v4.jar」「google-play-services.jar」を削除したうえで、次の方法でプッシュ通知等に必要なライブラリを配置します。

### Android SDKからNCMBが必要とするクラスの個別ライブラリを拾ってきて配置する

Android SDK Managerで「Google Repository」をインストールしておくと、\sdk\extras\google\m2repository\com\google\android\gms 下から各種Googleサービス用のaarファイル群が見つかります。
「play-services-gcm-XXX.aar」を見つけて、UnityプロジェクトのPlugins/Androidフォルダ直下に置きましょう。
(XXXはバージョン名、執筆時点での最新は10.2.6）

なお、play-servicesライブラリはAndroidフォルダ直下に置く必要があるそうです。

### Google Ads Pluginの場合：AdMobDependencies.csを書き換えて自動入手する
Google Ads Pluginには、必要なライブラリを自動的にダウンロードするマネージャーが入っています。
Assets\GoogleMobileAds\Editor\AdMobDependencies.csがそれで、デフォルトではplay-services-adsに関するライブラリをダウンロードする設定になっています。
これを改造します。次のメソッド呼び出しをstatic void SetupDeps()内に書き加えてください。

```csharp

        Google.VersionHandler.InvokeInstanceMethod(
            svcSupport, "DependOn",
            new object[] { "com.google.android.gms", "play-services-gcm",
                                   "LATEST" },
            namedArgs: new Dictionary<string, object>() {
                        {"packageIds", new string[] {
                                "extra-google-m2repository",
                                "extra-android-m2repository"} }
            });

```

play-services-adsを取得する処理をコピって、play-services-gcmに書き換えただけです。
書き換えが終わったらメニューのAssets -> Play Services Resolver -> Android Resolver -> Resolve Client Jarsを実行してください。
「play-services-gcm-XXX.aar」がAndroidフォルダ直下に配置されます。

## パターン３：他のAndroid SDKがライブラリ内で悪さしている

下記参照

【Unity】こんなAndroidネイティブプラグインをつくっちゃいけない！【特に広告SDK】
http://qiita.com/RyotaMurohoshi/items/3b95daeb8eaa6b487a20

> ネイティブプラグインのJavaのコードにはRクラスの定数はベタ書きしないでください。ResourcesクラスのgetIdentifierメソッドを用いて、idを取得してください。

NCMB SDK Unityはこの悪手を踏んでいないことを確認しています。
広告SDKを他にも入れている場合はそいつが悪さしてる可能性があります。

# どうすれば競合は改善するか
「AndroidManifestファイルは競合するので、ゼロから開発者にすべて書いてもらいましょう！」だと導入ハードルが上がるので、なんかいい感じに解決したいと考えています。

まず、NCMB Unity SDKはjarでライブラリを持っていますが、jarファイルを使ったプラグイン実装はUnity5では非推奨です。

```
OBSOLETE - Providing Android resources in Assets/Plugins/Android/res is deprecated, please move your resources to an Android Library. 
```
というワーニングが出るのはこのためです。

代わりにaarを使うようにするだけで、ライブラリの競合は一部解消できるでしょう。

~~さらに言えばsupport-v4-xxx.aarがすでに存在する場合は同梱のライブラリを削除する、という処理があれば完璧です。~~
(そもそもPlugins/Android直下に置かれたライブラリしかインクルードされないので、こういう処理は必要なかった。現状下のディレクトリに入っていてもビルドされるのはバグだそうです。)

~~NCMB Unity SDK 3.0.0からUnity 4の対応を切ったようなので、jar版は無くても良いかと考えています。
わたくしNCMBエヴァンジェリストをやっていますので、開発チームに要望を投げました。~~ 
要望が取り入れられて反映されました。やったぜ。

~~さて、そうした時の問題はAndroidManifestの扱いです。
aarはビルド時にUnpackingされ、内部のAndroidManifest設定により各種アクティビティを良しなに処理してくれます。
しかし、現状のNCMB Unity SDKはAndroidManifest.xml内にBundle Identifier(Package Name)を手動で書き込む仕様のため、
aarの中にAndroidManifestを入れといて自動的にマージ、という形ができません。

ビルド前の処理でAndroidManifestにPlayerSettings.applicationIdentifierから引っ張ってきたPackage Nameを追加して...みたいな設計が可能なのかどうか。このへん調査中です。

(そもそもBundle Identifierを別の手段で渡せないか？）~~

aar化でこのコンフリクトも解消されそうなので、調査中です。

# 参考にしたウェブサイト

AdMob と Nifty Cloud のプッシュ通知を共存させる [Unity]
http://b.i-tach.com/?p=1015

こちらはGoogle Mobile Ads Unity Plugin 3.0.5　+ NCMB Unity SDK 2.2.0の場合の対処法になります。