banner



How To Use A Service Android Studio Media Player And Create Only One Instance Of It

Many Android apps accept features that take a long fourth dimension to run, like downloading content or updating a remote database. If developers don't handle these features properly, they slowly clutter the main thread, creating retentiveness leaks and inconsistencies in the results. This leads to a bad user experience.

Fortunately, there'southward a solution for handling these actions: Android Services. This is a special type of application component that's designed to handle repetitive and long running jobs.

In this tutorial, you'll learn:

  • What Android Services are and how to declare them in the Manifest file.
  • How to use a foreground service while displaying a notification to the user.
  • What is it with background processing for circuitous piece of work that isn't related to the UI.
  • How to use a jump service.

Note: This tutorial assumes intermediate knowledge of Kotlin, Android and Android Studio. If you're new to Android development or oasis't installed Android Studio, refer to our Beginning Android Development tutorials.

Getting Started

Download the starter projection by using the Download Materials push at the tiptop or bottom of this tutorial.

For this tutorial, you'll use the Memo app. Memo is a game that challenges the user to match cards in the shortest possible time. Additionally, the user can listen to audio tracks while they play.

The game has four levels: Beginner, Intermediate, Advanced and Expert. The higher the level, the more than cards to match. After the user finishes the expert level, they consummate the game. Practice yous dare to try your luck? :]

Currently, the app merely lets the user beginning a game and manipulate the audio tracks. Once you start a game, you'll exist able to quit, preview game cards and runway the time elapsed since the game started.

Open the project in Android Studio and go familiar with the files. When you lot're done, build and run information technology on a device/emulator to come across how the app looks.

Memo's main screen

Play the game past pressing PLAY BEGINNER LEVEL. At this point, you lot tin friction match the cards simply there's no timer to show how long information technology took you to win. That's the first thing yous'll implement in this tutorial.

Beginner level of the Memo game

First, nevertheless, you lot'll take a deeper expect at Android Services.

Introducing Android Services

An Android Service is a component that helps execute long-running processes, like updating a database or server, running a countdown and playing sound. Past default, Android Services run in the aforementioned procedure as the main application thread does. This kind of service is besides known as a local service.

For resource-intensive tasks, the service executes the work on the background thread. This lets the user run actions without being disturbed. For example, if the user wants to make a telephone call or check letters while updating a server, they can do so without interrupting or aborting the update action.

To do this, the developer needs to create a background thread and manually specify which tasks should run on information technology considering the service doesn't directly support the thread implementation.

Since an Android Service doesn't take a user interface, it isn't spring to the activeness'south lifecycle. That means you can run information technology fifty-fifty when the user isn't interacting with the app.

Additionally, other components can demark to the service and interact with it, or fifty-fifty do InterProcess Communication (IPC). You'll acquire more than about IPC afterward in this tutorial.

In the following sections, y'all'll implement each of the below types of Android Services in the Memo app to achieve the post-obit features:

  • Foreground service: Add together TimerService, which contains logic to track time.
  • Jump service: Implement MusicService, which will allow you play, intermission, stop and shuffle the sound tracks.

It's time to get started!

Declaring a Service in the Manifest

Before using a service, you lot demand to declare it in AndroidManifest.xml. Afterwards all, it'south an Android component, simply similar Activities are. For simplicity, the starter project already contains the Service classes, but you notwithstanding demand to configure them properly.

Navigate to AndroidManifest.xml and add this code within application:

          <service       android:proper name=".services.TimerService"       android:enabled="true"       android:exported="false" />      <service       android:name=".services.MusicService"       android:enabled="truthful"       android:exported="false" />        

In this lawmaking, you see the following:

  • android:name: The name of the Service class.
  • android:enabled: When set to true allows the system to instantiate the services.
  • android:exported: When fix to false tells the arrangement that components of another application can't showtime these services.

The value of android:proper noun is in red, but only ignore that for now. You'll set up this shortly.

For now, yous'll kickoff with the implementation.

Implementing the Service Interface

Every custom service class must exist a subclass of Service and must implement and override a few basic callback methods connected to the service lifecycle.

The almost important of these are:

  • onStartCommand(): Chosen by the arrangement when another component wants to first a service using startService(). In one case the arrangement calls onStartCommand(), the service can run in the background for every bit long as it takes to complete. You need to call back to stop the service manually when the work is over past calling stopSelf() or stopService(). You lot don't need this implementation if you only want to bind to the service without an choice to outset it.
  • onBind(): You always need to implement this callback method. The arrangement invokes it when bindService() is chosen. Apply it to bind some other component to the service. It opens a communication, then you'll provide an interface for the clients to communicate past returning IBinder. If y'all don't desire to use binding, just render null.
  • onCreate(): Use this method to gear up up a service before it starts. The system invokes it only if the service isn't running however.
  • onDestroy(): The system invoke this to destroy the service when information technology is not needed anymore. It'south the last call the service receives and information technology cleans up the resources or threads.

Creating a Foreground Service

A foreground service executes tasks that the user needs to see. It shows a status bar notification to alert the user that an app is performing an operation and consuming resources. The notification must accept a priority of PRIORITY_LOW or higher and must be visible fifty-fifty when the user isn't interacting with the app.

The user can't dismiss the notification; information technology disappears when the service stops or leaves the foreground.

To encounter this in play, y'all'll implement TimerService. Open TimerService.kt and add the following to make it a subclass of Service:

class TimerService : Service(), CoroutineScope {        

With this lawmaking, you implement the Service and CoroutineScope interface.

Implementing the Service Methods

Await closely and notice that Android Studio is showing an error because onBind() is missing.

Android Studio provides an error that onBind() isn't implemented

To set up this, add together the following methods to TimerService:

          // 1  override fun onBind(intent: Intent): IBinder? = cypher   override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {    super.onStartCommand(intent, flags, startId)    // 2    intent?.extras?.run {      when (getSerializable(SERVICE_COMMAND) as TimerState) {        TimerState.Offset -> startTimer()        TimerState.Pause -> pauseTimerService()        TimerState.STOP -> endTimerService()        else -> return START_NOT_STICKY      }    }    // three    render START_NOT_STICKY  }   override fun onDestroy() {    super.onDestroy()    // 4    handler.removeCallbacks(runnable)    task.cancel()  }        

Here is a breakdown of the lawmaking:

  1. Since this is a foreground service, you don't need binding so you return null instead of IBinder. Also notation that you lot won't implement onCreate() now since y'all don't need whatever specific setup for the service.
  2. Here, you get through the Intent extras to become the value that the fundamental SERVICE_COMMAND saves. This value indicates which action the service should execute.
  3. If the system kills the service because the memory runs out, START_NOT_STICKY tells the arrangement not to recreate the service with an undefined Intent. Alternative constants are START_STICKY and START_REDELIVER_INTENT. If you're interested in these, check out the official service constants documentation.
  4. Remove all Handler callbacks for the ticking fourth dimension to continue the memory clean. Also, clean upwards the resource by canceling the whole timer Job since yous'll destroy the service in the adjacent pace.

Adding Permissions

All apps that target Android 9 (API level 28) or college must request permission to use a foreground service. The system automatically grants that request because it'southward a blazon of normal permission. Failure to do so leads the app to throw a SecurityException.

In Project view, navigate to app ▸ manifest ▸ AndroidManifest.xml and add together the following code above the application tag:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />        

This lets you lot run the foreground service in Memo.

Adjacent, y'all'll create a notification for the service.

Creating a Notification Channel

Every foreground service must notify the user that it has started. Y'all also need to define a Notification Manager, which notifies the user of events that happen and holds data well-nigh 1 or more than notifications.

Open NotificationHelper.kt and add this code at the top of the class:

individual val notificationManager past lazy {   context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }        

Hither, you lot're lazy initializing notificationManager by getting the NOTIFICATION_SERVICE organization service and casting it every bit NotificationManager.

In Android 8 and later, the notification must be role of a notification aqueduct. To create one, add this code to NotificationHelper.kt:

@RequiresApi(Build.VERSION_CODES.O) private fun createChannel() =     // i     NotificationChannel(       CHANNEL_ID,       CHANNEL_NAME,       NotificationManager.IMPORTANCE_DEFAULT     ).utilise {        // ii       description = CHANNEL_DESCRIPTION       setSound(null, null)     }        

In this code, you:

  1. Create a notification aqueduct with CHANNEL_ID as its ID, CHANNEL_NAME equally its name and the default importance.
  2. Ready the notification aqueduct'southward description to CHANNEL_DESCRIPTION and sound to nada which just ways that there is no sound played when a notification is triggered in the channel.

The next step is to define a notification view.

Creating a Notification Builder

For the user to be aware of the service running, create a view that the user can run across in the status bar. At the top of NotificationHelper.kt, add together the notification code:

          // 1   private val notificationBuilder: NotificationCompat.Architect by lazy {     NotificationCompat.Architect(context, CHANNEL_ID)        // ii       .setContentTitle(context.getString(R.string.app_name))       .setSound(cipher)       .setContentIntent(contentIntent)       .setSmallIcon(R.drawable.ic_launcher_foreground)       .setPriority(NotificationCompat.PRIORITY_HIGH)       // 3       .setAutoCancel(true)   }        

If you become an error saying contentIntent isn't defined, don't worry. You'll take intendance of information technology in the next steps.

For now, in this code:

  1. You're using NotificationCompat.Architect to create a status bar notification builder.
  2. The builder has to incorporate information about some of the notification's options. Notwithstanding, y'all don't have to ascertain the notification options when you declare a architect. If you're missing information about the title, audio and and then on, y'all can prepare that information later, in the notification itself.
  3. This kind of notification should be prepare equally car-cancelable, which means that when the user clicks it, it's automatically dismissed.

Note: Keep in heed that the mutual mistake developers make is forgetting to assign a small icon to the notification. This results in a subconscious notification.

Edifice a Notification

At present, information technology's time to build the notification itself. Add the following to NotificationHelper.kt:

          fun getNotification(): Notification {     // ane     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {       notificationManager.createNotificationChannel(createChannel())     }      // 2     return notificationBuilder.build()   }        

Hither'south what's happening higher up:

  1. If the version of Android is equal to or greater than Android viii, the arrangement creates a notification channel and returns Notification. Unfortunately, the Support Library for versions before Android 8 doesn't provide notification aqueduct APIs. Read more about this issue in the official notification channel documentation.
  2. Return a Notification subsequently invoking build() on the builder.

Notification can be dynamically updated. In this case, you'll update the text if the user closes the app while the service is running.

Below getNotification(), add the following:

fun updateNotification(notificationText: String? = zippo) {     // 1     notificationText?.let { notificationBuilder.setContentText(it) }     // ii     notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()) }        

Here'due south what's happening:

  1. You update the text in the notification published in the status bar via the notificationBuilder
  2. Then notify the Notification Manager about which notification to update. To do that, you employ a unique NOTIFICATION_ID.

Finally, you'll define what happens when a user clicks on the notification. Add this to the meridian of NotificationHelper.kt:

            private val contentIntent past lazy {     PendingIntent.getActivity(       context,       0,       Intent(context, MainActivity::form.java),       PendingIntent.FLAG_UPDATE_CURRENT     )   }        

Here, y'all're doing a lazy initialization of PendingIntent, which launches MainActivity when the user presses the notification.

Great task! You've done a lot of work already. Now, information technology's fourth dimension to really run the foreground service.

Starting and Stopping the Service

To kickoff the service in the foreground, use startForeground(). This method needs two parameters: the unique positive integer ID of the notification and Notificaton.

Open TimerService.kt and add the below to startTimer() method:

startForeground(NotificationHelper.NOTIFICATION_ID, helper.getNotification())        

This method makes the service run in the foreground and posts the notification with the ID of NOTIFICATION_ID to the status bar. You'll notice that helper is highlighted in red because you lot haven't divers that variable all the same.

Error: Unresolved reference for the notification helper class

To fix that, create an instance of NotificationHelper class at the top of TimerService:

private val helper by lazy { NotificationHelper(this) }        

To try starting the service from another component, open MainActivity.kt, discover sendCommandToForegroundService() and add:

ContextCompat.startForegroundService(this, getServiceIntent(timerState))        

Here, yous first the service from Activity. Yous laissez passer its Context and the Intent to get-go the service.

Everything that starts must end, and that includes Service. Open TimerService.kt and add the post-obit to stopService():

// one stopForeground(truthful)  // ii stopSelf()        

In this code block:

  1. This call tells the system that it should remove this service from foreground state. The passed boolean statement corresponds to removing the notification when ready to true
  2. Since a service tin kickoff itself, it must handle stopping itself, as well. stopSelf() handles that use case.

Updating the Notification When the Timer Changes

TimerService contains a coroutine that ticks every second while the level is running. You'll use the timer value to update the notification view.

Await for broadcastUpdate() within the TimerService.kt, and add this lawmaking inside the if:

// 1 sendBroadcast(   Intent(TIMER_ACTION)     .putExtra(NOTIFICATION_TEXT, elapsedTime) )  // 2 helper.updateNotification(   getString(R.string.time_is_running, elapsedTime.secondsToTime()) )        

Here is what this code cake does:

  1. Here, you transport a broadcast with the elapsed time to MainActivity. With it, MainActivity tin update the fourth dimension in the TextView below the card's view.
  2. This helper method updates the status bar notification you posted to a higher place.

At this point, you've implemented everything the user sees while playing the game. Merely what happens if the user kills the app while the timer is running?

To handle that, add the following line to broadcastUpdate() in the else if:

helper.updateNotification(getString(R.string.get_back))        

This line updates the notification text to call the user back to the game.

Awesome work! Build and run to test the timer. First a level and notice how the timer changes at the bottom. Pull downwardly the condition bar to see how the timer changes within the notification. Try exiting the application to test the bulletin text update in the notification.

Time ticks are visible on the screen

Using Background Processing for Complex Piece of work

Sometimes, the user doesn't need to know most certain actions happening, e.g. if you store the user's action on the server for developing purposes, you don't accept to display a notification. Place this kind of continuous piece of work and similar in the background.

To reach that, yous tin can use:

  1. Custom Groundwork Service – past default, the service runs on the UI thread. To avoid blocking information technology, create a Service with a chore processing on the groundwork thread.
  2. IntentService – a subclass of Service that executes requests sequentially by using worker thread. Since Android 8, information technology'due south usage is not recommended. Also, IntentService is deprecated from Android 11.
  3. JobIntentService – a replacement for IntentService. Instead of service, it uses JobScheduler for executing jobs. In earlier versions than Android 8, it will human action but similar IntentService. For more info, read the official JobIntentService documentation.
  4. Background work with WorkManager – this is a general concept for doing groundwork piece of work in Android. Utilize it when you want to execute a periodical job in the future, or when you have some job constraints.

Explaining Background Execution Limits

Android viii and newer versions accept restrictions when using Android Services on the background thread:

  1. The organization will destroy the service afterwards some time if the app runs the service in the background. In one case it goes to the background, information technology has a window of a few minutes in which it can interact with services. After that, the system kills all running services.
  2. The system will throw an exception if the service was started when the app is in the background and outside of the time window.

Find more in Background execution limits documentation.

Now, information technology's time to larn near the last kind of service.

Creating a Bound Service

A spring service is an implementation of Service. It'southward a component that other components demark to, interacting with it and performing interprocess communication (IPC). The bound component can be an Activity, some other service, a broadcast receiver or a content provider.

The lifecycle of the bound service depends on some other application component. When the component unbinds, the leap service destroys itself. Consequently, information technology tin can't run in the background forever.

However, it doesn't necessarily need a bound component — it can offset and cease itself as well. In that case, there's an selection to run indefinitely.

To try this out, yous'll give Memo the ability to play audio.

Converting MusicService into actual Service

To implement the audio playing characteristic, yous'll utilize MusicService. Firstly, modify it to accept admission to basic service methods.

Open MusicService.kt and brand it extend Service:

class MusicService : Service() {        

Equally its type says, some other component will use this service to demark to it. You already learned almost the mandatory method you need for binding to service, so add it to MusicService:

override fun onBind(intent: Intent?): IBinder = folder        

The deviation between the last implementation of onBind() and this 1 is, obviously, the return value. Since you're binding to service here, you need to provide the IBinder value.

IBinder is a programming interface of Folder that clients use to interact with the service. Its methods permit you lot to send a call to an IBinder object and receive a telephone call coming in to a Binder object. To learn more, bank check out IBinder'southward documentation.

Android Studio shows an error because y'all oasis't defined binder variable. To set this, add the following to the top of the grade:

private val binder by lazy { MusicBinder() }        

Here, you're doing a lazy initialization of binder, which is a type of MusicBinder. Implementation of MusicBinder is your side by side step.

Defining a Binder

This service can't be accessed exterior of this application and doesn't accept its ain process. Therefore, you take to create an interface that will take access to the service'due south context. Do that by extending the Binder course. This way all components can use all public methods from Binder or the Service.

Now, to define the interface, add these lines at the bottom of the class:

inner form MusicBinder : Binder() {    fun getService(): MusicService = this@MusicService }        

MusicBinder is nested inside another grade and it tin can access all methods from it. It tin can use all of Binder's methods as well. Within of it, you create a method for retrieving a service context.

Defining Service Methods for Managing Audio

This service allows the user to interact with the audio icons for playing, pausing, stopping and shuffling the music. For simplicity, the methods for managing audio are already divers. However, a music actor isn't.

To instantiate the music actor, utilize this lawmaking inside initializeMediaPlayer() inside MusicService.kt:

          musicMediaPlayer = MediaPlayer.create(this, randomSongs.first()).utilise {     isLooping = truthful   }        

Here, you use MediaPlayer to run a continuously repeating sound of the first song in randomSongs.

Another fun characteristic is that this bound service tin provide the name of the track that's playing. Inside MusicService, add:

fun getNameOfSong(): Cord =     resource.getResourceEntryName(randomSongs.first())         .replaceFirstChar {               if (it.isLowerCase()) it.titlecase(Locale.English)               else it.toString()          }.replace("_", " ")        

In this method, you're reading a track name from the resources and changing the result String to be more readable to the user.

All done! Now, you'll use these methods from another component.

Creating a Service Connection Callback

To use MusicService inside other classes, you have to create its example. Open up MainActivity.kt and add the following at the peak of the class:

individual var musicService: MusicService? = nada        

Hither, y'all're declaring a service variable that holds a service instance. For now, the service is nix. You lot'll assign a new value when the activity connects to the service past using the Binder interface. To catch the connection state changes, create a connection callback.

Add the lawmaking below the musicService initialization:

// 1 individual val boundServiceConnection = object : ServiceConnection {    // 2   override fun onServiceConnected(className: ComponentName, service: IBinder) {     val binder: MusicService.MusicBinder = service as MusicService.MusicBinder     musicService = binder.getService()     mainViewModel.isMusicServiceBound = truthful   }    // iii   override fun onServiceDisconnected(arg0: ComponentName) {     musicService?.runAction(MusicState.Finish)     musicService = null     mainViewModel.isMusicServiceBound = faux   } }        

That code is easy to assimilate:

  1. This is a callback for the service connection state.
  2. When activeness connects to service, the system uses MusicBinder example and getService() to give a reference to musicService.
  3. When service disconnects, the audio will end if the service reference isn't already null, immigration the service reference.

This sets a flag, isMusicServiceBound, that checks a service-spring land in both methods according to the parameter you provided. This is important to avoid DeadObjectException exceptions, which remote methods throw when the connection breaks.

Binding to the Service

Your next step is to bind the service when MainActivity starts. Find onStart() and add:

if (!mainViewModel.isMusicServiceBound) bindToMusicService()        

Here, yous're checking whether the service has a bounden already. If non, you call a binding method.

The binding method doesn't exist yet, and so add its code beneath onDestroy():

private fun bindToMusicService() {   // 1   Intent(this, MusicService::class.coffee).as well {     // 2     bindService(it, boundServiceConnection, Context.BIND_AUTO_CREATE)   } }        

In this code, yous:

  1. Declare an intent to start MusicService.
  2. Provide the Intent to the service along with the connection callback and a flag that automatically creates the service if the bounden exists.

To avert memory leaks or bugs, y'all need to add code to unbind the service. Add together the following to unbindMusicService():

unbindService(boundServiceConnection)        

With this, you tell the service to execute onServiceDisconnected() in the boundServiceConnection callback.

Using Service Methods

Once the service unbindes, y'all need to finish the music. Add the following lawmaking above the unbindService() line you just added:

musicService?.runAction(MusicState.STOP)        

Here, y'all employ the service instance to terminate the audio. Remember that you weren't able to call methods from TimerService. So, yous needed to provide flags through Intent. However, here you have a connexion — a binding — to the service, and so you're able to call its methods and receive a response.

To trigger an audio action, use sendCommandToBoundService(). Now, create a generic call for changing the action to reduce a number of code lines. Add this line to sendCommandToBoundService():

musicService?.runAction(state)        

With this, y'all tell the service to execute an action that matches the sendCommandToBoundService() parameter.

There is 1 more than thing to do! Once the song starts to play, the service can provide info about the song'south proper noun. Before you attempt that out, you demand to set the role with receiving a event.

Within getNameOfSong(), supersede the line which returns "Unknown" text with:

musicService?.getNameOfSong() ?: getString(R.string.unknown)        

Hither, you telephone call a method from the service that checks which audio track is currently playing and returns an optional Cord result. If the issue is zero, y'all use a resource text instead.

Build and run, so printing the Play icon to outset the audio. Press GET SONG Name to encounter the running song name in a Toast message. Finally, you lot can enjoy sound while playing your game!

Manipulating the MusicService actions

Interprocess Communication

Every Android app runs in its own process with a unique procedure ID. When you start an app, the Android system generates a process and runs a main activity on the chief thread. This has the advantage that the app lives in an isolated surround and other apps can't interrupt information technology.

Based on this, Android allows you to run components in a dissimilar process that isn't used to start an app. To exercise this, you need to employ the process tag inside AndroidManifest.xml. The procedure tin take a random name, like myNewProcess.

Multiprocessing gives your app better security and memory direction. If y'all use it, nevertheless, you lot demand to detect a style to communicate between different processes.

You but need to include i programming interface — IBinder — but Android provides 3 ways of defining information technology:

  • Extending Binder
  • Using a Messenger
  • Using AIDL

You lot already implemented the first approach in this tutorial, simply feel complimentary to investigate the other 2.

Where to Go From Here?

Download the terminal project using the Download Materials button at the peak or lesser of this tutorial.

You've learned a lot of new things about Android Services and how to implement them in different ways. Services are useful for app optimization and improving the user experience.

Here are some links yous can refer to:

  • Android Background Processing video tutorial
  • WorkManager Tutorial for Android
  • Android Notifications

Android 12 will bring some changes to foreground notifications. Check them out in the official foreground services documentation.

If yous take any questions or comments, feel costless to bring together the forum word below.

How To Use A Service Android Studio Media Player And Create Only One Instance Of It,

Source: https://www.raywenderlich.com/20123726-android-services-getting-started

Posted by: fosterretion1985.blogspot.com

0 Response to "How To Use A Service Android Studio Media Player And Create Only One Instance Of It"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel