The
Activity
in this app creates a
Service
which plays music with a
MediaPlayer
.
The
Service
and its music keep playing even after the
Activity
has been stopped or destroyed (e.g., by a
change
of orientation).
If the
Activity
is reborn, it’s smart enough to re-start the
Service
only if the
Service
is not already running.
By default, the
Service
is executed by the same thread as the
Activity
,
and we have done nothing to change this default.
For a simple example of a
Service
executed by a different thread, see
IntentService
and
Extending
the IntentService class.
MainActivity.java
uses an
Intent
to start a
Service
if the
Service
is not already running.
We saw an
Intent
here.
MediaPlayerService.java
activity_main.xml
strings.xml
musette.mid
.
Bach Musette in D major, BWV Anh. 126.
AndroidManifest.xml
.
When we created class
MediaPlayerService
,
Android Studio added a
<service>
element to the
<application>
element.
build.gradle
(Module: app)
Place
musette.mid
in
app/res/raw
,
as we did
here.
In the Android Studio
project
view,
select the folder
edu.nyu.scps.mediaplayerservice
.
File → New → Service → Service
Class Name: MediaPlayerService
Finish
Service
and
IntentService
.
App → Service → Local Service Controller
platform_development/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.java
defines class
LocalServiceActivities.Controller
(lines 33–78).
platform_development/samples/ApiDemos/src/com/example/android/apis/app/LocalService.java
defines class
LocalService
.
platform_development/samples/ApiDemos/res/layout/local_service_controller.xml
platform_development/samples/ApiDemos/res/values/strings.xml
local_service_started
(line 244)
is displayed in the status bar by the
showNotification
method of
LocalService
.
local_service_stopped
(line 245)
is displayed in a
Toast
by the
onDestroy
method of
LocalService
.
activity_local_service_controller
(line 248)
start_service
(line 251)
on the start button
stop_service
(line 252)
on the stop button
local_service_binding
(line 255)
stat_sample.png
is displayed by the
showNotification
method of
LocalService
.
It’s invisible against the black status bar,
but it shoves the other icons to the right.
platform_development/samples/ApiDemos/AndroidManifest.xml
declares class
LocalServiceActivities.Controller
in
lines 523–530.
onCreate
method of the
MainActivity
,
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> runningServices = activityManager.(Integer.MAX_VALUE); for (ActivityManager.RunningServiceInfo runningServiceInfo: runningServices) { Log.d("myTag", runningServiceInfo.service.getClassName()); }
com.google.android.backup.BackupTransportService com.android.systemui.ImageWallpaper com.google.android.location.geocode.GeocodeService com.google.android.gms.gcm.GcmService com.android.inputmethod.latin.LatinIME com.android.keyguard.KeyguardService com.google.android.location.fused.service.FusedProviderService com.google.android.gms.config.ConfigFetchService com.google.android.location.fused.FusedLocationService com.google.android.location.geofencer.service.GeofenceProviderService com.google.android.libraries.hangouts.video.CallService com.google.android.gms.icing.service.LightweightIndexService com.android.systemui.SystemUIService android.hardware.location.GeofenceHardwareService com.google.android.gms.icing.service.IndexWorkerService com.google.android.location.internal.PendingIntentCallbackService com.google.android.gms.icing.service.LightweightIndexService$LightweightWorkerService com.google.android.location.internal.server.GoogleLocationService com.google.android.location.network.NetworkLocationService com.google.android.location.internal.GoogleLocationManagerService com.google.android.gms.deviceconnection.service.DeviceConnectionServiceBroker com.android.smspush.WapPushManager com.google.android.gms.security.snet.SafetyNetClientService com.android.internal.backup.LocalTransportService com.google.android.gms.playlog.service.PlayLogBrokerService com.google.android.gms.common.stats.GmsCoreStatsService com.google.android.gms.clearcut.service.ClearcutLoggerService com.google.android.gms.config.ConfigService com.android.phone.TelephonyDebugService com.google.android.gms.icing.service.IndexService
adb
.
Type the following commands while the
MediaPlayerService
is running.
Minus lowercase L for list, minus lowercase H for help.
On Genymotion Samsung Galaxy S5:
adb -e shell dumpsys -l account activity alarm etc. adb -e shell dumpsys activity -h adb -e shell dumpsys activity services adb -e shell dumpsys activity services MediaPlayerService ACTIVITY MANAGER SERVICES (dumpsys activity services) User 0 active services: * ServiceRecord{52d80768 u0 edu.nyu.scps.mediaplayerservice/.MediaPlayerService} intent={cmp=edu.nyu.scps.mediaplayerservice/.MediaPlayerService} packageName=edu.nyu.scps.mediaplayerservice processName=edu.nyu.scps.mediaplayerservice baseDir=/data/app/edu.nyu.scps.mediaplayerservice-1.apk dataDir=/data/data/edu.nyu.scps.mediaplayerservice app=ProcessRecord{52aabd30 4717:edu.nyu.scps.mediaplayerservice/u0a98} createTime=-2s534ms startingBgTimeout=-- lastActivity=-2s534ms restartTime=-2s534ms createdFromFg=true startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=1
AudioManager
gives it permission to do so.
Compliance is voluntary,
but let’s be good citizens of the global community.
See
Handling
audio focus
and
Managing
Audio Focus.
Class
MediaPlayerService
should implement the interface
AudioManager.OnAudioFocusChangeListener
.
Add the following method to class
MediaPlayerService
.
@Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: //Resume playback. //Called only when focus is regained, not the first time focus is acquired. if (mediaPlayer == null) { mediaPlayer = MediaPlayer.create(this, R.raw.musette); } mediaPlayer.setVolume(1.0f, 1.0f); if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); } break; case AudioManager.AUDIOFOCUS_LOSS: //Lost focus for a long time. if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } mediaPlayer.release(); mediaPlayer = null; } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: //Lost focus for short time, but playback is likely to resume. if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: //Lost focus for short time, but can keep playing quietly. if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.setVolume(0.1f, 0.1f); } break; default: break; } }In the
Service
’s
onCreate
,
after creating the
MediaPlayer
:
mediaPlayer.setVolume(1.0f, 1.0f);
In
onStartCommand
,
replace the
mediaPlayer.start()
with the following.
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { mediaPlayer.start(); } else { Toast toast = Toast.makeText(MediaPlayerService.this, "focus not granted", Toast.LENGTH_LONG); toast.show(); }
Activity
called
startService
to start a
MediaPlayerService
that was already playing music?
Would two copies of the music be playing at the same time?