Contents
何を学習するのか?
AAC音楽ファイルの再生の為 Android ExoPlayerをUnityで
利用する為にKotlinを利用してAAR Native Plugin を書いてみます。
Simple Test AAR Unity Plugin Kotlin




Delete App Module


Copy Unity Class Library to Android Studio




1 2 |
// add gradle compileOnly fileTree(dir: 'libs', include: ['classes.jar']) |



Add Kotlin Class


Android Studio
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package net.yoshimax.apps.exoplayerpluginkt import android.util.Log class ExoPlayerPluginKt { fun test1() { Log.e("### ExoPlayerPluginKt ", "Test1") } fun test2(mes: String) { Log.e("### UnityExoPlayer Test2 ", mes) } fun test3(num: Int) { Log.e("####UnityExoPlayer Test3 ", num.toString()) } } |
Build Module to Make AAR File

Android Studio Output AAR Path

AAR Drug and Drop to Unity

Unity use AAR Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class ExoPlayerManager : MonoBehaviour { private AndroidJavaObject javaInstance; void Awake() { Debug.Log("##### Awake1"); using (AndroidJavaClass javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { using (var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) { javaInstance = new AndroidJavaObject("net.yoshimax.apps.unityexoplayerkt.UnityExoPlayerKt", currentActivity); } } } void Start() { Debug.Log("##### Start"); javaInstance.Call("test1"); javaInstance.Call("test3",1); javaInstance.Call("test2","DORAEMON"); } } |
Add Logcat Plugin On unity

Unity AAR Plugin ERROR
1 2 3 4 5 6 |
// OK javaInstance.Call("test1"); javaInstance.Call("test3",1); // ERROR javaInstance.Call("test2","DORAEMON"); |
1 |
2020/11/18 12:59:28.803 23092 23118 Error Unity AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics; |

How to Solve can’t access with argument.
Add Gradle

1 2 3 4 |
dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10' implementation fileTree(dir: 'libs', include: ['*.jar']) **DEPS**} |

now can access with argument Unity kotlin native plugin.
Add Android 10 Permission on Unity
ExoPlayer access local music File need permission.
Android Setting Write Permission to External (SDCard)

Android Manifest
1 2 |
// Add to Android Manifest on Android10 access External Storage android:requestLegacyExternalStorage="true" |




1 2 |
// add for Android10 <application android:requestLegacyExternalStorage="true"> |

1 |
2020/11/18 13:48:29.039 29589 29661 Error ExoPlayerImplInternal com.google.android.exoplayer2.upstream.FileDataSource$FileDataSourceException: java.io.FileNotFoundException: /storage/emulated/0/Music/KISSYOU.mp3: open failed: EACCES (Permission denied) |
For ExoPlayer
Android (ExoPlayerPluginKy.kt)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package net.yoshimax.apps.exoplayerpluginkt import android.content.Context import android.net.Uri import android.util.Log import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayerFactory import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import java.lang.Boolean class ExoPlayerPluginKt(context: Context) { private var player: ExoPlayer? = null private var factory: DefaultDataSourceFactory? = null private lateinit var myContext: Context init { playerInit(context) } private fun playerInit(context: Context) { Log.e("##### UnityExoPlayer playerInit ", "111111111111") myContext = context player = ExoPlayerFactory.newSimpleInstance( context, com.google.android.exoplayer2.trackselection.DefaultTrackSelector() ) factory = DefaultDataSourceFactory(context, "UnityExoPlayer") } fun prepare(path: String){ val uri = Uri.parse(path) // val uri = Uri.parse("/storage/emulated/0/Music/KISSYOU.mp3") val mediaSource = ProgressiveMediaSource.Factory( factory ).createMediaSource(uri) player?.playWhenReady = false player?.prepare(mediaSource, true, true) } fun playPause() { if ( player?.playbackState== Player.STATE_READY && player?.playWhenReady == Boolean.TRUE) { player?.playWhenReady = false } else { player?.playWhenReady = true } } } |
Android (build.gradle)

1 2 3 |
// Add dependencies { implementation 'com.google.android.exoplayer:exoplayer:2.10.4' |

Unity (mainTemplate.gradle)

1 2 3 |
dependencies { // ADD for ExoPlayer implementation 'com.google.android.exoplayer:exoplayer:2.10.4' |
Unity (ExoPlayermanager.cs)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ExoPlayerManager : MonoBehaviour { private AndroidJavaObject javaInstance; void Awake() { Debug.Log("##### Awake"); using (AndroidJavaClass javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { using (var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) { javaInstance = new AndroidJavaObject("net.yoshimax.apps.exoplayerpluginkt.ExoPlayerPluginKt", currentActivity); } } } // Start is called before the first frame update void Start() { Debug.Log("##### Start"); javaInstance.Call("prepare","/storage/emulated/0/Music/KISSYOU.mp3"); javaInstance.Call("playPause"); } // Update is called once per frame void Update() { } } |
Software Engineer #Unity #iOS