因此,我正在通过通知制作播客播放器并在前台运行媒体服务。一切顺利,除了我清除最近的应用程序。除非我事先使用了通知按钮(播放,暂停等),否则当我这样做时服务就会消失。我已经查看了所有内容以及我所得到的内容。任何帮助将非常感激。我已经包含了服务的整个代码,但实际上我认为问题位于buildNotification方法中。
public class PodcastService extends Service implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,
AudioManager.OnAudioFocusChangeListener {
private static MediaPlayer pp;
private static Episode episode = new Episode("", "49731298537982742984232", "", "", "", "", "");
private static Bitmap bitmap;
//Used to pause/resume MediaPlayer
private int resumePosition;
public static Boolean isBound = true;
private AudioManager audioManager;
private boolean shouldStart = true;
public static final String ACTION_PLAY = "com.campbellyamane.podantic.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.campbellyamane.podantic.ACTION_PAUSE";
public static final String ACTION_PREVIOUS = "com.campbellyamane.podantic.ACTION_PREVIOUS";
public static final String ACTION_NEXT = "com.campbellyamane.podantic.ACTION_NEXT";
public static final String ACTION_STOP = "com.campbellyamane.podantic.ACTION_STOP";
//MediaSession
private MediaSessionManager mediaSessionManager;
private MediaSession mediaSession;
private MediaController.TransportControls transportControls;
//AudioPlayer notification ID
private static final int NOTIFICATION_ID = 101;
private static Notification notification;
//Callbacks
private static Callbacks activity;
@Override
public void onAudioFocusChange(int focusState) {
//Invoked when the audio focus of the system is updated.
switch (focusState) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (pp == null) {
initMediaPlayer();
} else if (!pp.isPlaying() && shouldStart) {
pp.start();
}
pp.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (pp.isPlaying()) {
pp.pause();
shouldStart = true;
}
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (pp.isPlaying()) {
pp.pause();
shouldStart = true;
}
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (pp.isPlaying()) {
pp.setVolume(0.1f, 0.1f);
}
break;
}
}
private boolean requestAudioFocus() {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//Focus gained
return true;
}
//Could not gain focus
return false;
}
private boolean removeAudioFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
audioManager.abandonAudioFocus(this);
}
// Binder given to clients
private final IBinder iBinder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {
isBound = true;
return iBinder;
}
public class LocalBinder extends Binder {
public PodcastService getService() {
return PodcastService.this;
}
}
private void initMediaPlayer() {
if (pp == null) {
pp = new MediaPlayer();
//Set up MediaPlayer event listeners
pp.setOnCompletionListener(this);
pp.setOnErrorListener(this);
pp.setOnPreparedListener(this);
pp.setOnBufferingUpdateListener(this);
pp.setOnSeekCompleteListener(this);
pp.setOnInfoListener(this);
pp.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
}
public Boolean exists() {
return pp != null;
}
public Episode getPlaying() {
return episode;
}
public void playMedia(Episode e) {
activity.cbPreLoad();
if (requestAudioFocus()) {
episode = e;
try {
//Reset so that the MediaPlayer is not pointing to another data source
pp.reset();
pp.setDataSource(episode.getMp3());
pp.prepareAsync();
} catch (IOException ex) {
ex.printStackTrace();
} catch (IllegalStateException ex) {
try {
pp.setDataSource(episode.getMp3());
} catch (IOException e1) {
e1.printStackTrace();
}
pp.prepareAsync();
}
}
}
private void stopMedia() {
if (pp == null) {
return;
}
if (pp.isPlaying()) {
pp.stop();
}
}
public boolean isPlaying() {
return pp.isPlaying();
}
public void pauseMedia() {
if (pp.isPlaying()) {
pp.pause();
resumePosition = pp.getCurrentPosition();
shouldStart = false;
activity.cbSetPlay(false);
buildNotification(PlaybackStatus.PAUSED);
} else if (!pp.isPlaying()) {
pp.start();
activity.cbSetPlay(true);
buildNotification(PlaybackStatus.PLAYING);
}
}
public void rwMedia() {
if (pp != null) {
pp.seekTo(pp.getCurrentPosition() - 10000);
}
}
public void ffMedia() {
if (pp != null) {
pp.seekTo(pp.getCurrentPosition() + 10000);
}
}
public int getDuration() {
return pp.getDuration();
}
public int getCurrentPosition() {
try {
return pp.getCurrentPosition();
} catch (Exception e) {
return 0;
}
}
public void seekTo(int ms) {
pp.seekTo(ms);
}
@Override
public void onCompletion(MediaPlayer mp) {
//Invoked when playback of a media source has completed.
stopMedia();
//stop the service
stopSelf();
}
//Handle errors
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//Invoked when there has been an error during an asynchronous operation
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Log.d("MediaPlayer Error", "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.d("MediaPlayer Error", "MEDIA ERROR SERVER DIED " + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.d("MediaPlayer Error", "MEDIA ERROR UNKNOWN " + extra);
break;
}
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
//Invoked when the media source is ready for playback.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(episode.getArt());
bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (Exception e) {
e.printStackTrace();
}
activity.cbOnLoad();
buildNotification(PlaybackStatus.PLAYING);
pp.start();
}
});
pp = mp;
thread.start();
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
//Invoked indicating buffering status of
//a media resource being streamed over the network.
}
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
//Invoked to communicate some info.
return false;
}
@Override
public void onSeekComplete(MediaPlayer mp) {
//Invoked indicating the completion of a seek operation.
}
//The system calls this method when an activity, requests the service be started
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
initMediaPlayer();
try {
initMediaSession();
} catch (RemoteException e) {
e.printStackTrace();
}
handleIncomingActions(intent);
return super.onStartCommand(intent, flags, startId);
}
private void initMediaSession() throws RemoteException {
if (mediaSessionManager != null) return; //mediaSessionManager exists
mediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
// Create a new MediaSession
mediaSession = new MediaSession(getApplicationContext(), "PodcastPlayer");
//Get MediaSessions transport controls
transportControls = mediaSession.getController().getTransportControls();
//set MediaSession -> ready to receive media commands
mediaSession.setActive(true);
//indicate that the MediaSession handles transport control commands
// through its MediaSessionCompat.Callback.
mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
//Set mediaSession's MetaData
updateMetaData();
// Attach Callback to receive MediaSession updates
mediaSession.setCallback(new MediaSession.Callback() {
// Implement callbacks
@Override
public void onPlay() {
super.onPlay();
pauseMedia();
}
@Override
public void onPause() {
super.onPause();
pauseMedia();
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
//skipToNext();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
//skipToPrevious();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
@Override
public void onStop() {
super.onStop();
removeNotification();
//Stop the service
stopSelf();
}
@Override
public void onSeekTo(long position) {
super.onSeekTo(position);
}
});
}
public enum PlaybackStatus {
PLAYING,
PAUSED
}
public void buildNotification(PlaybackStatus playbackStatus) {
int notificationAction = android.R.drawable.ic_media_pause;//needs to be initialized
PendingIntent play_pauseAction = null;
//Build a new notification according to the current state of the MediaPlayer
if (playbackStatus == PlaybackStatus.PLAYING) {
notificationAction = android.R.drawable.ic_media_pause;
//create the pause action
play_pauseAction = playbackAction(1);
} else if (playbackStatus == PlaybackStatus.PAUSED) {
notificationAction = android.R.drawable.ic_media_play;
//create the play action
play_pauseAction = playbackAction(0);
}
// Create a new Notification
Notification.Builder notificationBuilder = (Notification.Builder) new Notification.Builder(this)
.setShowWhen(false)
// Set the Notification style
.setStyle(new Notification.MediaStyle()
// Attach our MediaSession token
.setMediaSession(mediaSession.getSessionToken())
// Show our playback controls in the compact notification view.
.setShowActionsInCompactView(0, 1, 2))
// Set the Notification color
.setColor(getResources().getColor(R.color.colorPrimary))
// Set the large and small icons
.setLargeIcon(bitmap)
.setSmallIcon(android.R.drawable.stat_sys_headset)
// Set Notification content information
.setContentText(episode.getPodcast())
.setContentTitle(episode.getTitle())
.setContentInfo(episode.getDate())
.setPriority(Notification.PRIORITY_MAX)
// Add playback actions
.addAction(android.R.drawable.ic_media_previous, "previous", playbackAction(3))
.addAction(notificationAction, "pause", play_pauseAction)
.addAction(android.R.drawable.ic_media_next, "next", playbackAction(2));
notificationBuilder.setOngoing(true);
notification = notificationBuilder.build();
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notification);
startForeground(NOTIFICATION_ID, notification);
}
public void removeNotification() {
stopForeground(true);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
}
private PendingIntent playbackAction(int actionNumber) {
Intent playbackAction = new Intent(this, PodcastService.class);
switch (actionNumber) {
case 0:
// Play
playbackAction.setAction(ACTION_PLAY);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 1:
// Pause
playbackAction.setAction(ACTION_PAUSE);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 2:
// Next track
playbackAction.setAction(ACTION_NEXT);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 3:
// Previous track
playbackAction.setAction(ACTION_PREVIOUS);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
default:
break;
}
return null;
}
private void handleIncomingActions(Intent playbackAction) {
if (playbackAction == null || playbackAction.getAction() == null) return;
String actionString = playbackAction.getAction();
if (actionString.equalsIgnoreCase(ACTION_PLAY)) {
transportControls.play();
} else if (actionString.equalsIgnoreCase(ACTION_PAUSE)) {
transportControls.pause();
} else if (actionString.equalsIgnoreCase(ACTION_NEXT)) {
transportControls.skipToNext();
} else if (actionString.equalsIgnoreCase(ACTION_PREVIOUS)) {
transportControls.skipToPrevious();
} else if (actionString.equalsIgnoreCase(ACTION_STOP)) {
transportControls.stop();
}
}
private void updateMetaData() {
// Update the current metadata
mediaSession.setMetadata(new MediaMetadata.Builder()
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)
.putString(MediaMetadata.METADATA_KEY_ARTIST, episode.getPodcast())
.putString(MediaMetadata.METADATA_KEY_ALBUM, episode.getDate())
.putString(MediaMetadata.METADATA_KEY_TITLE, episode.getTitle())
.build());
}
public void registerCallbacks(Activity activity) {
this.activity = (Callbacks) activity;
}
//callbacks interface for communication with service clients!
public interface Callbacks {
public void cbSetPlay(boolean play);
public void cbOnLoad();
public void cbPreLoad();
}
}