AndroidJavaException: java.lang.NoSuchMethodError に対応する
もうすぐ動物パニックパズルのリリースができそう(多分7月中に申請できるかな)なんです。その際にいくつかハマった罠とその対処法を備忘録として残しておきます。
実行環境
MacbookPro 2018 15inch
Unity 2019.4.2f1 LTS
Mobile Notifications 1.3.0
UnityのMobile Notificationsを実装
ハイパーカジュアルゲーム で果たしてPush通知が必要なのだろうかと思ったけど、実装しました。
この時、UnityのMobile Notificationsパッケージを使って実装しました。これはサーバーサイドプッシュ通知システムではなく、あくまでローカルプッシュ通知を実装するためのパッケージです。
Mobile Notifications は2020/7/6現在だと1.0.3がverified状態ですが、その上に1.3.0があるのでそちらを利用しました。
しかし、このパッケージを使ってプッシュ通知を実装してAndroid版のバイナリを内部テスト可能なバージョンとしてアップロードして実行しても、待てど暮らせどプッシュ通知が来ません。logcatで調べると以下の例外エラーが発生していました。
E/Unity ( 5948): AndroidJavaException: java.lang.NoSuchMethodError: no static method with name='getNotificationManagerImpl' signature='(Landroid.app.Application;Lcom.unity3d.player.UnityPlayerActivity;)Ljava/lang/Object;' in class Lcom.unity.androidnotifications.UnityNotificationManager;
E/Unity ( 5948): java.lang.NoSuchMethodError: no static method with name='getNotificationManagerImpl' signature='(Landroid.app.Application;Lcom.unity3d.player.UnityPlayerActivity;)Ljava/lang/Object;' in class Lcom.unity.androidnotifications.UnityNotificationManager;
E/Unity ( 5948): at com.unity3d.player.ReflectionHelper.getMethodID(Unknown Source)
E/Unity ( 5948): at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
E/Unity ( 5948): at com.unity3d.player.UnityPlayer.access$300(Unknown Source)
E/Unity ( 5948): at com.unity3d.player.UnityPlayer$e$1.handleMessage(Unknown Source)
E/Unity ( 5948): at android.os.Handler.dispatchMessage(Handler.java:98)
E/Unity ( 5948): at android.os.Looper.loop(Looper.java:136)
E/Unity ( 5948): at com.unity3d.player.UnityPlayer$e.run(Unknown Source)
解決策
結論から言うと、Minify処理が原因でした。
Proguard設定している場合、proguard-user.txtで対象クラスを設定しておけば大丈夫。
Proguard設定を見直す
Minify設定をProguardにしている場合、proguard-user.txtを設定しないとMinify処理対象にされてしまい、クラスが迷子になってしまうようです。これは特にネイティブライブラリをリフレクションコール(?)するような処理があると影響が出てきます。
今回は、Mobile Notificationsライブラリを利用していますので、AndroidのネイティブコードとiOSのネイティブコードが同梱されているので、対象のクラスもろともMinify対象にされてしまうみたいです。
その場合、proguard-user.txtに除外して欲しくないパッケージを設定することで該当クラスのMinifyを回避することができました。
proguard-user.txt を設定する
Custom Proguard File にチェックをすると、Assets/Plugins/Android/proguard-user.txt のファイルを参照するようになりますので、ここにproguard-user.txtファイルを作成し、次のような設定をしておきます。
-keep class com.google.android.gms.games.multiplayer.** { *; }
-keep class com.google.android.gms.games.leaderboard.** { *; }
-keep class com.google.android.gms.games.snapshot.** { *; }
-keep class com.google.android.gms.games.achievement.** { *; }
-keep class com.google.android.gms.games.event.** { *; }
-keep class com.google.android.gms.games.stats.** { *; }
-keep class com.google.android.gms.games.video.** { *; }
-keep class com.google.android.gms.games.* { *; }
-keep class com.google.android.gms.common.api.ResultCallback { *; }
-keep class com.google.android.gms.signin.** { *; }
-keep class com.google.android.gms.dynamic.** { *; }
-keep class com.google.android.gms.dynamite.** { *; }
-keep class com.google.android.gms.tasks.** { *; }
-keep class com.google.android.gms.security.** { *; }
-keep class com.google.android.gms.base.** { *; }
-keep class com.google.android.gms.actions.** { *; }
-keep class com.google.games.bridge.** { *; }
-keep class com.google.android.gms.common.ConnectionResult { *; }
-keep class com.google.android.gms.common.GooglePlayServicesUtil { *; }
-keep class com.google.android.gms.common.api.** { *; }
-keep class com.google.android.gms.common.data.DataBufferUtils { *; }
-keep class com.google.android.gms.games.quest.** { *; }
-keep class com.google.android.gms.nearby.** { *; }
-keep class com.unity.androidnotifications.** { *; }
僕はPlayGameServicesも利用しているので、その設定も入っていますが、最後の行にMobile Notificationsのライブラリも追加しておきます。
これで再ビルドして実行した結果、上記の例外が発生しなくなりちゃんとプッシュ通知が来るようになりました。
あとがき
この設定をしないで回避する方法は、Minifyを止めることですが、コードサイズを小さくしたり難読化を諦めることになってしまうので覚えておいて損はないと思う。
とにも角にも無事実装できたので、
めでたし。めでたし。です。
マジ、ありがとうございます!駆け出し開発者ですが、とても助かりました!
私も助かりました。
Unityも全部書いてくれたらよいのにと思います。
めちゃ助かりました!!