This article will show you how to communicate between activity and background service in android. It will play, pause and stop a web audio file in the android background service in this example. It will also update the activity progress bar from the background service to display the audio playing process.
1. Activity Communicate With Background Service Required Components.
- From the above picture, we can see to call and control background service in the android-activity, you need below four component.
- Activity ( PlayBackgroundAudioActivity ): In this activity we use the below code to bind android background service ( AudioService ) with this activity.
Intent intent = new Intent(PlayBackgroundAudioActivity.this, AudioService.class); // Below code will invoke serviceConnection's onServiceConnected method. bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
- ServiceConnection: This is an instance of the android.content.ServiceConnection class, it’s onServiceConnected method is invoked when the activity binds audio service. In that method, it will cast and assign an iBinder input parameter to the activity’s object variable.
- Background Service ( AudioService ): It only contains an instance of AudioServiceBinder ( the fourth component ), and return this binder instance in it’s onBind method.
- Custom Binder Class ( AudioServiceBinder ): This class is a subclass of android.os.Binder. It will implement all audio player start, pause and stop methods.
2. Play Web Audio Fie In Background Service Example.
If you can not watch the above video, you can see it on the youtube URL https://youtu.be/wy8pzsf4L6w
- Below is the example files list.
D:\WORK\DEV2QA.COM-EXAMPLE-CODE\ANDROIDEXAMPLEPROJECT\EXAMPLE\APP\SRC\MAIN AndroidManifest.xml D:\WORK\DEV2QA.COM-EXAMPLE-CODE\ANDROIDEXAMPLEPROJECT\EXAMPLE\APP\SRC\MAIN\JAVA\COM\DEV2QA\EXAMPLE\AUDIO\BACKGROUND AudioService.java AudioServiceBinder.java PlayBackgroundAudioActivity.java
2.1 Main Activity Java File.
- PlayBackgroundAudioActivity.java.
- ServiceConnection is a private instance variable of this class.
package com.dev2qa.example.audio.background; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.dev2qa.example.R; public class PlayBackgroundAudioActivity extends AppCompatActivity { private AudioServiceBinder audioServiceBinder = null; private Handler audioProgressUpdateHandler = null; // Show played audio progress. private ProgressBar backgroundAudioProgress; private TextView audioFileUrlTextView; // This service connection object is the bridge between activity and background service. private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // Cast and assign background service's onBind method returned iBander object. audioServiceBinder = (AudioServiceBinder) iBinder; } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_play_background_audio); setTitle("dev2qa.com - Play Audio Use Background Service"); // Bind background audio service when activity is created. bindAudioService(); final String audioFileUrl = "http://www.dev2qa.com/demo/media/test.mp3"; backgroundAudioProgress = (ProgressBar)findViewById(R.id.play_audio_in_background_service_progressbar); // Get audio file url textview. audioFileUrlTextView = (TextView)findViewById(R.id.audio_file_url_text_view); if(audioFileUrlTextView != null) { // Show web audio file url in the text view. audioFileUrlTextView.setText("Audio File Url. \r\n" + audioFileUrl); } // Click this button to start play audio in a background service. Button startBackgroundAudio = (Button)findViewById(R.id.start_audio_in_background); startBackgroundAudio.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Set web audio file url audioServiceBinder.setAudioFileUrl(audioFileUrl); // Web audio is a stream audio. audioServiceBinder.setStreamAudio(true); // Set application context. audioServiceBinder.setContext(getApplicationContext()); // Initialize audio progress bar updater Handler object. createAudioProgressbarUpdater(); audioServiceBinder.setAudioProgressUpdateHandler(audioProgressUpdateHandler); // Start audio in background service. audioServiceBinder.startAudio(); backgroundAudioProgress.setVisibility(ProgressBar.VISIBLE); Toast.makeText(getApplicationContext(), "Start play web audio file.", Toast.LENGTH_LONG).show(); } }); // Click this button to pause the audio played in background service. Button pauseBackgroundAudio = (Button)findViewById(R.id.pause_audio_in_background); pauseBackgroundAudio.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { audioServiceBinder.pauseAudio(); Toast.makeText(getApplicationContext(), "Play web audio file is paused.", Toast.LENGTH_LONG).show(); } }); // Click this button to stop the media player in background service. Button stopBackgroundAudio = (Button)findViewById(R.id.stop_audio_in_background); stopBackgroundAudio.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { audioServiceBinder.stopAudio(); backgroundAudioProgress.setVisibility(ProgressBar.INVISIBLE); Toast.makeText(getApplicationContext(), "Stop play web audio file.", Toast.LENGTH_LONG).show(); } }); } // Bind background service with caller activity. Then this activity can use // background service's AudioServiceBinder instance to invoke related methods. private void bindAudioService() { if(audioServiceBinder == null) { Intent intent = new Intent(PlayBackgroundAudioActivity.this, AudioService.class); // Below code will invoke serviceConnection's onServiceConnected method. bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } } // Unbound background audio service with caller activity. private void unBoundAudioService() { if(audioServiceBinder != null) { unbindService(serviceConnection); } } @Override protected void onDestroy() { // Unbound background audio service when activity is destroyed. unBoundAudioService(); super.onDestroy(); } // Create audio player progressbar updater. // This updater is used to update progressbar to reflect audio play process. private void createAudioProgressbarUpdater() { /* Initialize audio progress handler. */ if(audioProgressUpdateHandler==null) { audioProgressUpdateHandler = new Handler() { @Override public void handleMessage(Message msg) { // The update process message is sent from AudioServiceBinder class's thread object. if (msg.what == audioServiceBinder.UPDATE_AUDIO_PROGRESS_BAR) { if( audioServiceBinder != null) { // Calculate the percentage. int currProgress =audioServiceBinder.getAudioProgress(); // Update progressbar. Make the value 10 times to show more clear UI change. backgroundAudioProgress.setProgress(currProgress*10); } } } }; } } }
2.2 Background Service Class.
- AudioService.java
package com.dev2qa.example.audio.background; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class AudioService extends Service { private AudioServiceBinder audioServiceBinder = new AudioServiceBinder(); public AudioService() { } @Override public IBinder onBind(Intent intent) { return audioServiceBinder; } }
2.3 Binder Class.
- AudioServiceBinder.java
package com.dev2qa.example.audio.background; import android.content.Context; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import java.io.IOException; public class AudioServiceBinder extends Binder { // Save local audio file uri ( local storage file. ). private Uri audioFileUri = null; // Save web audio file url. private String audioFileUrl = ""; // Check if stream audio. private boolean streamAudio = false; // Media player that play audio. private MediaPlayer audioPlayer = null; // Caller activity context, used when play local audio file. private Context context = null; // This Handler object is a reference to the caller activity's Handler. // In the caller activity's handler, it will update the audio play progress. private Handler audioProgressUpdateHandler; // This is the message signal that inform audio progress updater to update audio progress. public final int UPDATE_AUDIO_PROGRESS_BAR = 1; public Context getContext() { return context; } public void setContext(Context context) { this.context = context; } public String getAudioFileUrl() { return audioFileUrl; } public void setAudioFileUrl(String audioFileUrl) { this.audioFileUrl = audioFileUrl; } public boolean isStreamAudio() { return streamAudio; } public void setStreamAudio(boolean streamAudio) { this.streamAudio = streamAudio; } public Uri getAudioFileUri() { return audioFileUri; } public void setAudioFileUri(Uri audioFileUri) { this.audioFileUri = audioFileUri; } public Handler getAudioProgressUpdateHandler() { return audioProgressUpdateHandler; } public void setAudioProgressUpdateHandler(Handler audioProgressUpdateHandler) { this.audioProgressUpdateHandler = audioProgressUpdateHandler; } // Start play audio. public void startAudio() { initAudioPlayer(); if(audioPlayer!=null) { audioPlayer.start(); } } // Pause playing audio. public void pauseAudio() { if(audioPlayer!=null) { audioPlayer.pause(); } } // Stop play audio. public void stopAudio() { if(audioPlayer!=null) { audioPlayer.stop(); destroyAudioPlayer(); } } // Initialise audio player. private void initAudioPlayer() { try { if (audioPlayer == null) { audioPlayer = new MediaPlayer(); if (!TextUtils.isEmpty(getAudioFileUrl())) { if (isStreamAudio()) { audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } audioPlayer.setDataSource(getAudioFileUrl()); } else { audioPlayer.setDataSource(getContext(), getAudioFileUri()); } audioPlayer.prepare(); // This thread object will send update audio progress message to caller activity every 1 second. Thread updateAudioProgressThread = new Thread() { @Override public void run() { while(true) { // Create update audio progress message. Message updateAudioProgressMsg = new Message(); updateAudioProgressMsg.what = UPDATE_AUDIO_PROGRESS_BAR; // Send the message to caller activity's update audio prgressbar Handler object. audioProgressUpdateHandler.sendMessage(updateAudioProgressMsg); // Sleep one second. try { Thread.sleep(1000); }catch(InterruptedException ex) { ex.printStackTrace(); } } } }; // Run above thread object. updateAudioProgressThread.start(); } }catch(IOException ex) { ex.printStackTrace(); } } // Destroy audio player. private void destroyAudioPlayer() { if(audioPlayer!=null) { if(audioPlayer.isPlaying()) { audioPlayer.stop(); } audioPlayer.release(); audioPlayer = null; } } // Return current audio play position. public int getCurrentAudioPosition() { int ret = 0; if(audioPlayer != null) { ret = audioPlayer.getCurrentPosition(); } return ret; } // Return total audio file duration. public int getTotalAudioDuration() { int ret = 0; if(audioPlayer != null) { ret = audioPlayer.getDuration(); } return ret; } // Return current audio player progress value. public int getAudioProgress() { int ret = 0; int currAudioPosition = getCurrentAudioPosition(); int totalAudioDuration = getTotalAudioDuration(); if(totalAudioDuration > 0) { ret = (currAudioPosition * 100) / totalAudioDuration; } return ret; } }
2.4 Layout XML File.
- activity_play_background_audio.xml
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/audio_file_url_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textStyle="bold" android:textSize="20dp" /> <Button android:id="@+id/start_audio_in_background" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Play Audio In Background Service" android:textAllCaps="false"/> <Button android:id="@+id/pause_audio_in_background" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Pause Audio In Background Service" android:textAllCaps="false"/> <Button android:id="@+id/stop_audio_in_background" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Audio In Background Service" android:textAllCaps="false"/> <ProgressBar android:id="@+id/play_audio_in_background_service_progressbar" style="@android:style/Widget.ProgressBar.Horizontal" android:max="100" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="invisible"/> </LinearLayout>
2.5 Android Manifest Xml File.
- The background service component must be declared in the AndroidManifest.xml file.
- Because this example plays web audio from a URL, so it needs android.permission.INTERNET permission to be declared in this file also.
- AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.dev2qa.example"> <!-- Play web url audio file required permission. --> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".audio.background.PlayBackgroundAudioActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".audio.background.AudioService" android:enabled="true" android:exported="true"></service> </application> </manifest>
Reference
- Android Play Local / URL Audio With ProgressBar Example
- How To Create, Start, Stop Android Background Service
What for screen rotation changed?
its not playing any audio
It works fantastic. This is what I have been looking for a while now. There few things I have noticed when i tested it. It plays the audio twice(2) when I open the app again while its already playing and hit play button. How can we solve this?
Hi, hope u are ok. I have the same problem. Did you solve it? Thank you.
Perfect!!
Tutorial ready !! how could I make the play and pause controls in the background in the notification bar using this example? Would you help me?
Nice work.
Can you remove progressbar and add forward and backward button.
And also read mp3 from phone not in the url.
Thanks for your tutorial im new in java.