我试了好几次都没有成功。
即使应用程序被刷了,我也希望音乐能够继续播放,就像Google音乐一样。
我可能会缺少什么?以下是此类。
MusicService
public class MediaPlayerService extends Service implements
MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,
AudioManager.OnAudioFocusChangeListener {
private MediaPlayer mediaPlayer;
private final IBinder iBinder = new LocalBinder();
public void onCreate() {
super.onCreate();
initMediaPlayer();
callStateListener();
registerBecomingNoisyReceiver();
register_playNewAudio();
}
public static final String MusicServiceTag = "MusicTest";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
StorageUtil storage = new StorageUtil(getApplicationContext());
audioList = storage.loadAudio();
audioIndex = storage.loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
} catch (NullPointerException e) {
stopSelf();
}
//Request audio focus
if (!requestAudioFocus()) {
//Could not gain focus
stopSelf();
}
if (mediaSessionManager == null) {
try {
initMediaSession();
initMediaPlayer();
} catch (RemoteException e) {
e.printStackTrace();
stopSelf();
}
buildNotification(PlaybackStatus.PLAYING);
}
//Handle Intent action from MediaSession.TransportControls
handleIncomingActions(intent);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
//Disable the PhoneStateListener
if (phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
removeNotification();
//unregister BroadcastReceivers
unregisterReceiver(becomingNoisyReceiver);
unregisterReceiver(playNewAudio);
//clear cached playlist
new StorageUtil(getApplicationContext()).clearCachedAudioPlaylist();
}
private int resumePosition;
private void initMediaPlayer() {
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnInfoListener(this);
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
System.out.println(MusicServiceTag + "File :: " + activeAudio);
String x = "https://upload.wikimedia.org/wikipedia/commons/6/6c/Grieg_Lyric_Pieces_Kobold.ogg";
mediaPlayer.setDataSource(activeAudio == null ? x : activeAudio.getData());
mediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
stopSelf();
}
}
private void playMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void stopMedia() {
if (mediaPlayer == null) return;
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
private void pauseMedia() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
resumePosition = mediaPlayer.getCurrentPosition();
}
}
private void resumeMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(resumePosition);
mediaPlayer.start();
}
}
@Override
public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
}
@Override
public boolean onInfo(MediaPlayer mediaPlayer, int i, int i1) {
return false;
}
@Override
public void onSeekComplete(MediaPlayer mediaPlayer) {
}
public class LocalBinder extends Binder {
public MediaPlayerService getService() {
return MediaPlayerService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
@Override
public void onCompletion(MediaPlayer mp) {
stopMedia();
stopSelf();
}
private static final String TAG_ERROR = "MediaPlayer Error";
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Log.d(TAG_ERROR, "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.d(TAG_ERROR, "MEDIA ERROR SERVER DIED " + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.d(TAG_ERROR, "MEDIA ERROR UNKNOWN " + extra);
break;
}
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
playMedia();
}
//Audio Focus
private AudioManager audioManager;
//List of available Audio files
private ArrayList<Sound> audioList;
private int audioIndex = -1;
private Sound activeAudio; //an object of the currently playing audio
@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 (mediaPlayer == null) initMediaPlayer();
else if (!mediaPlayer.isPlaying()) mediaPlayer.start();
mediaPlayer.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 (mediaPlayer.isPlaying()) mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
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 (mediaPlayer.isPlaying()) mediaPlayer.pause();
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 (mediaPlayer.isPlaying()) mediaPlayer.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);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
private boolean removeAudioFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
audioManager.abandonAudioFocus(this);
}
//Becoming noisy
private BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//pause audio on ACTION_AUDIO_BECOMING_NOISY
pauseMedia();
buildNotification(PlaybackStatus.PAUSED);
}
};
private void registerBecomingNoisyReceiver() {
//register after getting audio focus
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(becomingNoisyReceiver, intentFilter);
}
//Handle incoming phone calls
private boolean ongoingCall = false;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
//Handle incoming phone calls
private void callStateListener() {
// Get the telephony manager
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
//Starting listening for PhoneState changes
phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
//if at least one call exists or the phone is ringing
//pause the MediaPlayer
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
if (mediaPlayer != null) {
pauseMedia();
ongoingCall = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
// Phone idle. Start playing.
if (mediaPlayer != null) {
if (ongoingCall) {
ongoingCall = false;
resumeMedia();
}
}
break;
}
}
};
// Register the listener with the telephony manager
// Listen for changes to the device call state.
telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
}
private BroadcastReceiver playNewAudio = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Get the new media index form SharedPreferences
audioIndex = new StorageUtil(getApplicationContext()).loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
//index is in a valid range
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
//A PLAY_NEW_AUDIO action received
//reset mediaPlayer to play the new Audio
stopMedia();
mediaPlayer.reset();
initMediaPlayer();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
};
private void register_playNewAudio() {
//Register playNewMedia receiver
IntentFilter filter = new IntentFilter(MainActivity.Broadcast_PLAY_NEW_AUDIO);
registerReceiver(playNewAudio, filter);
}
//Notifications
public static final String ACTION_PLAY = "com.valdioveliu.valdio.audioplayer.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.valdioveliu.valdio.audioplayer.ACTION_PAUSE";
public static final String ACTION_PREVIOUS = "com.valdioveliu.valdio.audioplayer.ACTION_PREVIOUS";
public static final String ACTION_NEXT = "com.valdioveliu.valdio.audioplayer.ACTION_NEXT";
public static final String ACTION_STOP = "com.valdioveliu.valdio.audioplayer.ACTION_STOP";
//MediaSession
private MediaSessionManager mediaSessionManager;
private MediaSessionCompat mediaSession;
private MediaControllerCompat.TransportControls transportControls;
//AudioPlayer notification ID
private static final int NOTIFICATION_ID = 101;
private void initMediaSession() throws RemoteException {
if (mediaSessionManager != null) {
return; //mediaSessionManager exists
}
mediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
// Create a new MediaSession
mediaSession = new MediaSessionCompat(getApplicationContext(), "AudioPlayer");
//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(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
//Set mediaSession's MetaData
updateMetaData();
// Attach Callback to receive MediaSession updates
mediaSession.setCallback(new MediaSessionCompat.Callback() {
// Implement callbacks
@Override
public void onPlay() {
super.onPlay();
resumeMedia();
buildNotification(PlaybackStatus.PLAYING);
}
@Override
public void onPause() {
super.onPause();
pauseMedia();
buildNotification(PlaybackStatus.PAUSED);
}
@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);
}
});
}
private void updateMetaData() {
Bitmap albumArt = BitmapFactory.decodeResource(getResources(), R.drawable.music_placeholder);
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt);
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, activeAudio.getArtist());
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, activeAudio.getAlbum());
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, activeAudio.getTitle());
mediaSession.setMetadata(builder.build());
}
private void skipToNext() {
if (audioIndex == audioList.size() - 1) {
//if last in playlist
audioIndex = 0;
activeAudio = audioList.get(audioIndex);
} else {
//get next in playlist
activeAudio = audioList.get(++audioIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
private void skipToPrevious() {
if (audioIndex == 0) {
//if first in playlist
//set index to the last of audioList
audioIndex = audioList.size() - 1;
activeAudio = audioList.get(audioIndex);
} else {
//get previous in playlist
activeAudio = audioList.get(--audioIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
private void buildNotification(PlaybackStatus playbackStatus) {
int pauseIcon = R.drawable.exo_controls_pause;
int notificationAction = pauseIcon;
PendingIntent play_pauseAction = null;
if (playbackStatus == PlaybackStatus.PLAYING) {
notificationAction = pauseIcon;
play_pauseAction = playbackAction(1);
} else if (playbackStatus == PlaybackStatus.PAUSED) {
notificationAction = R.drawable.exo_controls_play;
play_pauseAction = playbackAction(0);
}
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.music_placeholder);
// Create a new Notification
MediaControllerCompat mediaControllerCompat = mediaSession.getController();
MediaMetadataCompat mediaMetadataCompat = mediaControllerCompat.getMetadata();
MediaDescriptionCompat mediaDescriptionCompat = mediaMetadataCompat.getDescription();
android.support.v4.app.NotificationCompat.Builder builder = MediaStyleHelper.from(getBaseContext(), mediaSession);
builder.setShowWhen(false);
builder.setStyle(new NotificationCompat.MediaStyle());
//builder.setMediaSession(mediaSession.getSessionToken());
//builder.setShowActionsInCompactView(0, 1, 2);
builder.setContentTitle(mediaDescriptionCompat.getTitle());
builder.setContentText(mediaDescriptionCompat.getSubtitle());
//builder.setContentIntent(contentIntent());
builder.setVisibility(android.support.v4.app.NotificationCompat.VISIBILITY_PUBLIC);
builder.setLargeIcon(mediaDescriptionCompat.getIconBitmap());
builder.setSmallIcon(R.drawable.icon);
builder.setColor(ContextCompat.getColor(this, R.color.colorPrimaryDark));
builder.setPriority(Notification.PRIORITY_MAX);
builder.addAction(R.drawable.exo_controls_previous, "previous", playbackAction(3));
builder.addAction(notificationAction, "pause", play_pauseAction);
builder.addAction(R.drawable.exo_controls_next, "next", playbackAction(2));
startForeground(453, builder.build());
}
private void removeNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
}
private PendingIntent playbackAction(int actionNumber) {
Intent playbackAction = new Intent(this, MediaPlayerService.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();
}
}
}
MediaStyleHelper
public class MediaStyleHelper {
public static NotificationCompat.Builder from(Context context, MediaSessionCompat mediaSession) {
MediaControllerCompat controller = mediaSession.getController();
MediaMetadataCompat mediaMetadata = controller.getMetadata();
MediaDescriptionCompat description = mediaMetadata.getDescription();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "channel_1");
builder.setContentTitle(description.getTitle());
builder.setContentText(description.getSubtitle());
builder.setSubText(description.getDescription());
builder.setContentIntent(controller.getSessionActivity());
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setWhen(0);
builder.setShowWhen(false);
if (description.getIconBitmap() == null || description.getIconBitmap().isRecycled()) {
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.music_placeholder));
} else {
builder.setLargeIcon(description.getIconBitmap());
}
return builder;
}
public static PendingIntent getActionIntent(Context context, int mediaKeyEvent) {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.setPackage(context.getPackageName());
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, mediaKeyEvent));
return PendingIntent.getBroadcast(context, mediaKeyEvent, intent, 0);
}
}
StorageUtil
public class StorageUtil {
private final String STORAGE = " com.domain.pack.core.media.STORAGE";
private SharedPreferences preferences;
private Context context;
public StorageUtil(Context context) {
this.context = context;
}
public void storeAudio(ArrayList<Sound> arrayList) {
preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
Gson gson = new Gson();
String json = gson.toJson(arrayList);
editor.putString("audioArrayList", json);
editor.apply();
}
public ArrayList<Sound> loadAudio() {
preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
Gson gson = new Gson();
String json = preferences.getString("audioArrayList", null);
Type type = new TypeToken<ArrayList<Sound>>() {
}.getType();
return gson.fromJson(json, type);
}
public void storeAudioIndex(int index) {
preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("audioIndex", index);
editor.apply();
}
public int loadAudioIndex() {
preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
return preferences.getInt("audioIndex", -1);//return -1 if no data found
}
public void clearCachedAudioPlaylist() {
preferences = context.getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.clear();
editor.commit();
}
}
// 编辑在MainActivity中启动服务
private void playAudio(int audioIndex) {
//Check is service is active
if (!serviceBound) {
//Store Serializable audioList to SharedPreferences
StorageUtil storage = new StorageUtil(getApplicationContext());
storage.storeAudio(audioList);
storage.storeAudioIndex(audioIndex);
Intent playerIntent = new Intent(this, MediaPlayerService.class);
startService(playerIntent);
bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//Store the new audioIndex to SharedPreferences
StorageUtil storage = new StorageUtil(getApplicationContext());
storage.storeAudioIndex(audioIndex);
//Service is active
//Send a broadcast to the service -> PLAY_NEW_AUDIO
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
sendBroadcast(broadcastIntent);
}
}
服务连接
//Binding this Client to the AudioPlayer Service
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
player = binder.getService();
serviceBound = true;
Toast.makeText(MainActivity.this, "Service Bound", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};