概要
Unityべったりで書いていて、ネイティブのAndroid javaやSwiftは書いたことない人が、急に何らかのOS標準機能に手を出す必要が出た場合の話
調べ物のアプローチについて
まずはAssetSoreで同様の機能を持ったアセットが無いか探す
急に必要になったということは、締め切りが近いはずです。まずいです。今から頑張ってAndroidStudioをインストールして初めて起動して、豪華なIDEにビビる余裕が無い可能性が高いです。
幸いな事にUnityは豊富なアセットストア資産があるので、割といろんなネイティブプラグインが存在します。落ち着いて探してみましょう。
お金で解決できるのは、とても素敵です(UnityAssetStoreライセンスが緩いのも魅力です)
自分で作るのは仕事が落ち着いた時に改めてやれば良いです。
AssetStoreに見つからなかった、どうするのこれ
落ち着いてGithubで参考になるプロジェクトを探しましょう。探すための手掛かりの関数名とかは、StackOverflowで逆引き的に検索するのがお勧めです。
Githubでドンピシャなプロジェクトが見つかったら、感謝の気持ちを持ちつつライセンスに従って利用させていただきましょう。
Githubにも無い…自分で作るしかないですかねこれ
はい、やっていきましょう。これが現実<リアル>です。
iOSの方はともかくとして(?) Androidの取っ掛かりとしてどこのサイトを見て作り始めれば良いか。
いきなり「アクティビティって何?」とか「全然わからない…俺たちは雰囲気でAndroidManifest.xmlを編集している…」みたいな人に向けた、ある程度まとまったサイトとして
【更新完了】(3) Android StudioでUnity向けにmoduleを作るときのトラブル対応集 - Cross Road
の記事内の1から順に追試することをお勧めします。
どうやら2017年現在において、
- 古き良きEclipseを使ったビルドは主流ではないこと(AndroidStudioを使う)
- aarとjarがあるものの、AndroidStudioからaarを出力したら、そこをバラしてjarも取り出せること
- aarだと中に含まれているAndroidManifestのマージ地獄がつらいのでjarの方が小回りが利きそうなこと
あたりが分かってきます。
アクティビティを拡張しよう
通知を受け取ったり、ハードウェアキーをフックしたり、いろんな事をネイティブプラグインで実現したくなって調べると、
プラグインのjavaソースの中でUnityPlayerActivity (もしくはUnityNativePlayerActivity) を拡張したアクティビティを作ろう、と 気軽に 書いてあります。
こんな感じですね!
public class HogeHogeSuperUltraCoolMyPluginActivity extends UnityPlayerActivity
{
@Override
protected void onCreate(Bundle bundle) {
Log.e("[Unity Test]","Extended Activity Created");
super.onCreate(bundle);
}
}
アクティビティって何だ?と言うのが引っかかりますが、Unityで言うと 要は メイン画面 です。
もしくは単一のエントリーポイント、もっと古い例えだとCで言うmain関数みたいなものです。
UnityはUnityPlayerActivityと言うUnity独自で拡張しまくったActivityを持っています。
そのActivityを更に拡張して、独自のプラグイン機能を仕込んでいこう、という訳ですね。
UnityのAndroidアプリは、初期値ではUnityPlayerActivityから起動します。拡張した HogeHogeSuperUltraCoolMyPluginActivity から起動してほしい場合は、みんなが大好きな AndroidManifest.xmlの
<activity android:name=~~~~~>
をHogeHogeSuperUltraCoolMyPluginActivityに書き換えます。
NiftyCloud-mbaasさんだとこんな感じにAndroidManifestを設定してます。
https://github.com/NIFTYCloud-mbaas/ncmb_unity/blob/master/ncmb_unity/Assets/Plugins/Android/AndroidManifest.xml#L25
firebaseさんだとこんな感じにAndroidManifestを設定してます
Unity を使用して Firebase Cloud Messaging クライアント アプリを設定する | Firebase
同様にFacebookSDKなんかもアクティビティを拡張していますし、ARライブラリのVuforia SDKもアクティビティを拡張しています。
色んなSDKで使われている技術のようですね。すごい!
ようこそUnityPlayerActivity拡張コンフリクト地獄へ
さて「単一のエントリポイントを拡張する」技術が「色んなSDKで使われている」わけです。
小さなアプリや、全部自前で書く場合は気にしなくても大丈夫でしょうし、Unityネイティブプラグイン入門 なら問題なさそうです。
とあるシチュエーション
開発者Aさんが、Vuforiaを使ったちょっとしたARアプリをUnityでAndroid 向けに作っていました。
勉強会や展示会でデモしていると「すごい!これを是非売ってくれないか! Facebook連携と アプリ内html表示 さえ追加してくれたら5000兆円で買いたい!」と石油王が声をかけてきました。
Aさんは(FacebookはUnitySDKくらいあるだろうし、Unityアプリ内のhtml表示も greeのwebviewがあるから大丈夫だな!ヨッシャ5000兆円!!!!)と考え「はい!じゃあすぐ追加してお渡ししますよ!」と答えました。
想定される未来
AさんはUnityプロジェクト内に
- VuforiaがカスタムしたUnityPlayerActivityVuforia
- FacebookSDKがカスタムしたUnityPlayerActivityFaceBook
- webviewがカスタムしたUnityPlayerActivityWebview
の3個のカスタムUnityPlayerActivityを抱えることになりました。ところで、UnityPlayerActivityは単一のエントリポイントなので拡張したUnityPlayerActivityHogeHogeも1個しか使えませんね
はい…Aさんはどうするのでしょうか?
幸福な結末
こういう事態は、とても悲しいことですが現実世界では往々にしてよく起きます。そうなった時の一番マシな解決法は
UnityPlayerActivityFaceBookが標準のUnityPlayerActivityではなく、VudoriaのUnityPlayerActivityVuforiaを継承するようにして
UnityPlayerActivityWebviewが標準のUnityPlayerActivityではなく、UnityPlayerActivityFaceBookを継承するようにします。
こんな感じですね!
UnityPlayerActivity -> UnityPlayerActivityVuforia -> UnityPlayerActivityFaceBook -> UnityPlayerActivityWebview
できました!!!!
こうしてUnityPlayerActivityの拡張関係を入れ子構造にしていくことで、最後のUnityPlayerActivityWebviewだけをAndroidManifest.xmlで指定してあげれば、全SDKが共存できそうです。
マトリョーシカアプローチですね!
FaceBookSDKもWebviewも jarファイルの中身のプロジェクトまでオープンソースだったおかげで、この入れ子構造に書き換えてjarを再ビルドすることで助かりました!
良かったですねAさん!5000兆円おめでとうございます。
不幸な結末
ここまで読まれた賢明な(そしておそらく暇な)読者の方は「え?マトリョーシカアプローチとか入れ子って、ライブラリの中のjavaを書き換えてjarを再ビルドするんですか。ということは jarのソースコードが非公開だったらどうするの? 」と気になられたと思います。
大丈夫です。 中身がソース非公開なjarが一個だけなら それを入れ子構造の一番下にしてやれば解決しますって!
良かったですねAさん、ライブラリのビルドがちょっと大変かもしれないけど5000兆円貰えるならやる気も出るし、大丈夫ですって!
…はい、ソース非公開でアクティビティを拡張するjar が2個以上ある 場合の話をします。
これ、どうするんですかね…もうjarをバラしてdecompileして再ビルドしてやるしかないんじゃないですかね…
まあ5000兆円貰えるなら、優秀な人にお金いっぱい払って上記のデコンパイルして改変ビルド作業も丸投げしてもらう、とか、同等機能のSDKを自前で再実装する、とか出来ると思います。
じゃあ、現実世界のAさんが、石油王じゃなくて怖いクライアントさんで、納期が1週間で、予算が100万円しか無かったら、どうするんでしょうね?
Aさんと同じ境遇にある人たちの様子です。
Android Plugin. multiple Android Activity problems. | Unity Community
Facebook SDK for Unity | Vuforia Developer Portal
UnityのAndroid版ビルドで起動アクティビティを変更したい · Issue #593 · NIFTYCloud-mbaas/UserCommunity · GitHub
終わりに
@ 現場の方々
こういったシチュエーションの解決方法をご存知の方、教えてください。不幸なAさんを救ってあげてください。
@ SDK提供する人
少なくともUnityPlayerActivityを拡張するSDKについては、該当のjarのソースコードも提供してくださいお願いします。
@ ネイティブプラグインを作る時の作法としてUnityPlayerActivityを使う方法を考えた人
助けて
@ 新規にUnityAndroidでネイティブプラグインを組み込んだプロジェクトに参加する人
各SDKやプラグインのUnityPlayerActivity拡張部分を大至急確認しろ
最後になりましたが、こういったUnityPlayerActivity拡張以外でも、同一の機能を実装することは出来ます。
UnityPlayerActivityを直接触らずに、こういったアプローチであれば、何個ものライブラリが共存できます。
Unity Android plugin tutorial (1/3) Fundamentals - eppz!
出来れば、こちらの方式がメジャーになってほしいなあ…
オマケ
分かりにくかったかもしれないのでソース非公開のメインアクティビティ拡張なネイティブプラグインが2個以上ある時の結論は
「jarをdecompileしてアクティビティ名決め打ちを書き換えて再ビルドして、マトリョーシカアプローチ」
です。人類の叡智の敗北って感じがしますが、仕方ないですね。
アクティビティ拡張時の確認
このサンプルが一番最小限です。
AARやJarにしなくても良い
言葉通りです。
Unity2018.2以降であれば *.javaのネイティブプラグインをそのまま SomeDirectory/Plugins/Android/ 以下に配置出来ます。
この作法はアクティビティ拡張時にも有効です。
アクティビティ拡張時の protected void onCreateは @ override が無くても動く
言葉通りです。
これはそもそもそういう物ですね…
アクティビティ拡張時の拡張元はUnityPlayerActivity
Unity5.x系とかで一時期別の物を指定する作法があった気がしますが、普通に
public class SampleOverrideActivity extends UnityPlayerActivity {
で良いです。
アクティビティ拡張をするjavaのクラス名とファイル名は合わせる
うっかり SugoiPlugin.java
みたいな名前のファイルにしているのに、中の宣言が
//Activityって付けたくなるよね…でもファイル名と統一してね
public class SugoiPluginActivity extends UnityPlayerActivity {
だったりすると、クラスが見つけられなくなります。
AndroidManifest.xmlのアクティビティ名は相対パスが書ける
<activity android:name=
の指定はこの書き方も出来ます。
ただし、この書き方をする場合は /Assets/Plugins/Android 以下にコードを書く必要があります。
(つまり、 /Assets/SugoiModule/Plugins/Android 以下にあると機能しない)
<activity android:name=".SampleOverrideActivity"
android:label="@string/app_name"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
そもそも
https://docs.unity3d.com/ja/2018.4/Manual/AndroidAARPlugins.html
Unity は、 Assets/Plugins/Android の全てのサブフォルダーを、 Android ライブラリである可能性があるものとして扱う
と書いてあるので、たぶん配置は Assets/SugoiModule/Plugins/Android ではなく Assets/Plugins/Android の方が良い気がします。
アクティビティ拡張が通っていない場合apkをzip解凍して見る
ちゃんとapkをデコンパイルするのが面倒くさい時はzip解凍して出てきたAndroidManifest.xmlをチェックすることでも確認できます。
http://schemas.android.com/apk/res/android
の手前が