I’m trying to implement media3 MediaSession and mediaController. I’ve created mediaservice as described in official documentation. as described in official documentation, I’m supposed to use use a lateinit var for mediacontroller and then initialize it listener for mediaControllerFuture in onStart function override. However my app is crashing and I’m getting error : lateinit property mediaController has not been initialized
. I’ve seen other code on stackoverflow they’ve implemented it same way but somehow its working for them.
Problem is that official documentation does not seem very helpful as it assumes many thing a developer to know already and most of the stuff it telss is more for xml layout than jetpack compose and I’m trying to develop in compose.
Here is my MainActivity.kt
class MainActivity : ComponentActivity() {
private lateinit var mediaController: MediaController
private lateinit var controllerFuture: ListenableFuture<MediaController>
private fun play(){
val mediaItem = MediaItem.Builder().setMediaId("https://sd2.djjohal.com/128/519928/Sip%20Sip%20-%20Geeta%20Zaildar%20(DJJOhAL.Com).mp3")
.build()
mediaController.setMediaItem(mediaItem)
mediaController.prepare()
mediaController.play()
}
override fun onStart() {
super.onStart()
val sessionToken = SessionToken(this, ComponentName(this,AudioPlayBackService::class.java))
controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener({
mediaController = controllerFuture.get()
},
MoreExecutors.directExecutor()
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Media3testTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Text(text = mediaController.toString())
PlayPause(
modifier = Modifier.padding(innerPadding),
isPlaying = false,
onPlayPause = {
play()
}
)
}
}
}
}
override fun onStop() {
MediaController.releaseFuture(controllerFuture)
super.onStop()
}
}
@Composable
fun PlayPause(
modifier: Modifier = Modifier,
isPlaying: Boolean,
onPlayPause: () -> Unit
) {
IconButton(modifier = modifier, onClick = { onPlayPause()}) {
if (isPlaying) {
Icon(Icons.Default.PlayArrow, contentDescription = "Pause" )
}else{
Icon(Icons.Default.Person,"Play")
}
}
}
This is my AudioPlayBackService.kt file:
class AudioPlayBackService: MediaSessionService(), MediaSession.Callback {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player: Player = ExoPlayer.Builder(this)
.build()
mediaSession = MediaSession
.Builder(this, player)
.setCallback(this)
.setId("BeatBloomPlayBackService")
.build()
}
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<MutableList<MediaItem>> {
val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.mediaId).build() }.toMutableList()
return Futures.immediateFuture(updatedMediaItems)
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
mediaSession?.player?.prepare()
return mediaSession
}
override fun onDestroy() {
super.onDestroy()
mediaSession?.run {
player.release()
release()
mediaSession = null
}
}
}
this is my manifest file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Media3test"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Media3test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".player.AudioPlayBackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
</application>
</manifest>
I’ve tried searching for similar issues on stackoverflow, but most of here are not using jetpack compose or something entirely different.