Unity 6に上げたらAndroidビルドが壊れた話

117 Views

March 28, 25

スライド概要

2025/03/06に開催されたイベント、「ぷちTechCon for Unity」 で発表したスライドです。
イベント概要:https://dena.connpass.com/event/339748/

メジャーバージョンアップに伴い、さまざまな変更が加えられることがあります。
その中でAndroidのビルドシステムの変更により正常にUnityの動作が行われなくなることがあります。
今回の発表では、その問題がどうして起きたかとその対処法を紹介します

profile-image

DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

Unity 6に上げたら Androidビルドが壊れた話 菊地 勇輔 © DeNA Co., Ltd. 1

2.

自己紹介 菊地 勇輔 DeNA ソリューション本部 社内ではハンドルネームのラビで通しています モバイル全般エンジニアやってます 最近は Swift を書くことが多いです (今日やる内容と矛盾してない...?) @rabbitbooster @rabbitbooster © DeNA Co., Ltd. 2

3.

アジェンダ 1 それはUnity 6に上げたときに 2 原因と対応 3 さいごに © DeNA Co., Ltd. 3

4.

それはある日... © DeNA Co., Ltd. 4

5.

今年は自分がバージョンアップの担当 「数行直したらコンパイルエラーは修正したし 実機で確認してみるかー」 © DeNA Co., Ltd. 5

6.

Few Days Later…. © DeNA Co., Ltd. 6

7.

動かんが... © DeNA Co., Ltd. 7

8.

Few Days Later…. © DeNA Co., Ltd. 8

9.

動かない !! 😇 © DeNA Co., Ltd. 9

10.

アプリが期待通りに動作しない! 1 初期化時にエラーが発生し、クラッシュ Logcat(Androidのログシステム)で見ると以下のエラーが また、kotlinx-coroutines-android は元々追加してあるのに入れてない表記...? © DeNA Co., Ltd. 10

11.

なぜ起きたか? © DeNA Co., Ltd. 11

12.

2 なぜ起きた? 問題のコード private fun loadMainDispatcher(): MainCoroutineDispatcher { return try { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } https://github.com/Kotlin/kotlinx.coroutines/blob/1.10.1/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt © DeNA Co., Ltd. 12

13.

2 なぜ起きた? 問題のコード private fun loadMainDispatcher(): MainCoroutineDispatcher { return try { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } この値がTrueなら ライブラリ が用意している Fast Class Loader を利用 https://github.com/Kotlin/kotlinx.coroutines/blob/1.10.1/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt © DeNA Co., Ltd. 13

14.

2 なぜ起きた? 問題のコード private fun loadMainDispatcher(): MainCoroutineDispatcher { return try { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } Falseなら Java側のClassLoaderを利用 https://github.com/Kotlin/kotlinx.coroutines/blob/1.10.1/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt この制御はR8(難読化ツール)の Proguard File で制御 © DeNA Co., Ltd. 14

15.

なぜ起きた? 2 Unity 2022以前 private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true) private fun loadMainDispatcher(): MainCoroutineDispatcher { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } systemPropの定義は (propertyName,defaultValue)です 今まではライブラリ内にある Proguard File が読み込めていなかったため FAST_SERVICE_LOADER_ENABLED が true(初期値)に設定 © DeNA Co., Ltd. 15

16.

なぜ起きた? 2 Unity 2022以前 private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true) private fun loadMainDispatcher(): MainCoroutineDispatcher { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } Corutine ライブラリが提供している FastServiceLoader が使われていた © DeNA Co., Ltd. 16

17.

なぜ起きた? 2 Unity 6 では private fun loadMainDispatcher(): MainCoroutineDispatcher { val factories = if (FAST_SERVICE_LOADER_ENABLED) { FastServiceLoader.loadMainDispatcherFactory() } else { ServiceLoader.load( MainDispatcherFactory::class.java, MainDispatcherFactory::class.java.classLoader ).iterator().asSequence().toList() } Proguard File を読み込むようになって ○ © DeNA Co., Ltd. https://github.com/Kotlin/kotlinx.coroutines/blob/c08fa744f4f40cf94926d9d7de3cec78 6a2087ce/ui/kotlinx-coroutines-android/resources/META-INF/com.android.tools/r8-fro m-1.6.0/coroutines.pro FAST_SERVICE_LOADER_ENABLED が false に設定 ■ Java 側に用意されている Service Loader を利用 17

18.

なぜ起きた? 2 Java側のServiceLoaderについて ● サービス・プロバイダは、リソース・ディレクトリ META-INF/services にプロバイダ構成ファイル を配置することによって識別されます。 引用:https://docs.oracle.com/javase/jp/8/docs/api/java/util/ServiceLoader.html サンプルアプリのビルド中の処理に APKファイル内のMETA-INF 部分で何かをしてい たことを思い出し、その部分を一旦ビルドフローから削除 →動作するように!! © DeNA Co., Ltd. 18

19.

なぜ起きた? 2 なにをMETA-INF/配下でしていたか - サンプルアプリではAPKファイル内の署名関係を外部で行なっていた。 - サンプルアプリは META-INF/配下 を全て削除し、再署名していた →この際に必要なMETA-INF/servicesも削除して動かなくなっていた! © DeNA Co., Ltd. 19

20.

根本的な原因 2 Unityのバージョンアップで内部の Gradle のバージョンが 7系から8系に上がったこと が原因 - その中で完全に原因が特定はできていないのですが、R8 の FullMode が原因の可能性が高いと考えています (変更がさらっと書かれていますがNativeプラグインを使っていると、被害甚大なことが多い...) © DeNA Co., Ltd. 20

21.

対応 2 対応方法としては次の二つがあります。 1. META-INF/* で削除しないようにビルドする - 2. 署名関係ならCERT.SFとCERT.RSAだけを削除する処理に変更すれば良いです proguard 設定で FAST_SERVICE_LOADER_ENABLED を true にする (今回はこちらを採用) © DeNA Co., Ltd. 21

22.

2 FAST_SERVICE_LOADER_ENABLED を true にする方法 ● proguard-user.txt を利用する ○ Unity側Proguardファイルを追加すること が可能 ○ ライブラリより優先されるので 今回の場合でも上書き可能 ○ © DeNA Co., Ltd. -assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader { boolean FAST_SERVICE_LOADER_ENABLED return true; } 今回の場合のproguard-user.txtの中身 Assets/Plugins/Android/に配置 22

23.

3 最後に ● Unityのバージョン更新をするとGradleのバージョンアップがある ○ ● Unity側のリリースノートだけでは見えない部分もある ○ ● Native Pluginを利用していると影響を受けることが結構あります Native Pluginを使う場合 AndroidNative 部分とUnity側の両ドメインで知見が必要 難読化をしないといけない場合 R8 と戦う必要がある場合はあります ○ © DeNA Co., Ltd. しょうがない、頑張りましょう... 23

24.

© DeNA Co., Ltd. 24