Play background music in all activities of Android app

后端 未结 5 904
隐瞒了意图╮
隐瞒了意图╮ 2020-12-07 01:03

I spend about 20 hours until now and my problem there still is . I am creating an android application that has several Activities (mainMenu , aboutUs,setting). I followed th

相关标签:
5条回答
  • 2020-12-07 01:42

    Simon's answer above is correct. I had similar problem where I have fragments which had music player and I needed to go back to that UI on click of a button. Your case is similar but instead of going back to UI, you want to control playback. Here is what I did for my application. This takes care of playback of audio list including shuffle and repeat functionality. This takes care of showing media controls in notification bar too.

    1. Create a service MusicPlayerService with following code:
    public class MediaPlayerService extends Service implements MediaPlayer.OnCompletionListener,
                MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
                MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,
        
                AudioManager.OnAudioFocusChangeListener {
                
                
            public static final String ACTION_PLAY = "pkg_name.ACTION_PLAY";
            public static final String ACTION_PAUSE = "pkg_name.ACTION_PAUSE";
            public static final String ACTION_PREVIOUS = "pkg_name.ACTION_PREVIOUS";
            public static final String ACTION_NEXT = "pkg_name.ACTION_NEXT";
            public static final String ACTION_STOP = "pkg_name.ACTION_STOP";
        
            private MediaPlayer mediaPlayer;
        
            //MediaSession
            private MediaSessionManager mediaSessionManager;
            private MediaSessionCompat mediaSession;
            private MediaControllerCompat.TransportControls transportControls;
        
            //AudioPlayer notification ID
            private static final int NOTIFICATION_ID = 101;
        
            //Used to pause/resume MediaPlayer
            private int resumePosition;
        
            // Binder given to clients
            private final IBinder iBinder = new LocalBinder();
        
            //List of available Audio files
            private ArrayList<PlayableTrack> audioList;
            private int audioIndex = -1;
        
        
            //Handle incoming phone calls
            private boolean ongoingCall = false;
            private PhoneStateListener phoneStateListener;
            private TelephonyManager telephonyManager;
            private Bitmap albumArtBitmap;
            private boolean shuffle = false;
            private boolean repeat = false;
            private Random rand;
        
            /**
             * Service lifecycle methods
             */
            @Override
            public IBinder onBind(Intent intent) {
                return iBinder;
            }
        
            @Override
            public void onCreate() {
                super.onCreate();
                // Perform one-time setup procedures
        
                // Manage incoming phone calls during playback.
                // Pause MediaPlayer on incoming call,
                // Resume on hangup.
                callStateListener();
                //ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs -- BroadcastReceiver
                registerBecomingNoisyReceiver();
                //Listen for new Audio to play -- BroadcastReceiver
                register_playNewAudio();
                rand = new Random();
                StorageUtil storage = new StorageUtil(getApplicationContext());
                shuffle = storage.loadShuffleRepeat("Shuffle");
                repeat = storage.loadShuffleRepeat("Repeat");
            }
        
            //The system calls this method when an activity, requests the service be started
            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                try {
        
                    //Load data from SharedPreferences
                    StorageUtil storage = new StorageUtil(getApplicationContext());
                    audioList = storage.loadAudio();
                    audioIndex = storage.loadAudioIndex();
        
                    if (audioIndex != -1 && audioIndex < audioList.size()) {
                        //index is in a valid range
                        activeAudio = audioList.get(audioIndex);
                    } else {
                        stopSelf();
                    }
                } catch (NullPointerException e) {
                    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 boolean onUnbind(Intent intent) {
                mediaSession.release();
                removeNotification();
                return super.onUnbind(intent);
            }
        
            @Override
            public void onDestroy() {
                super.onDestroy();
                if (mediaPlayer != null) {
                    stopMedia();
                    mediaPlayer.reset();
                }
                //Disable the PhoneStateListener
                if (phoneStateListener != null) {
                    telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
                }
        
                removeNotification();
        
                //unregister BroadcastReceivers
                unregisterReceiver(becomingNoisyReceiver);
                unregisterReceiver(playNewAudio);
        
                Picasso.get().cancelRequest(target);
            }
        
            /**
             * Service Binder
             */
            public class LocalBinder extends Binder {
                public MediaPlayerService getService() {
                    // Return this instance of LocalService so clients can call public methods
                    return MediaPlayerService.this;
                }
            }
        
        
            /**
             * MediaPlayer callback methods
             */
            @Override
            public void onBufferingUpdate(MediaPlayer mp, int percent) {
                //Invoked indicating buffering status of
                //a media resource being streamed over the network.
            }
        
            @Override
            public void onCompletion(MediaPlayer mp) {
                //Invoked when playback of a media source has completed.
                stopMedia();
            }
        
            @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.
                playMedia();
            }
        
            @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.reset();
                        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;
                }
            }
        
            /**
             * MediaPlayer actions
             */
            private void initMediaPlayer() {
                if (mediaPlayer == null)
                    mediaPlayer = new MediaPlayer();//new MediaPlayer instance
        
                //Set up MediaPlayer event listeners
                mediaPlayer.setOnCompletionListener(this);
                mediaPlayer.setOnErrorListener(this);
                mediaPlayer.setOnPreparedListener(this);
                mediaPlayer.setOnBufferingUpdateListener(this);
                mediaPlayer.setOnSeekCompleteListener(this);
                mediaPlayer.setOnInfoListener(this);
                //Reset so that the MediaPlayer is not pointing to another data source
                mediaPlayer.reset();
        
                mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .build());
                try {
                    // Set the data source to the mediaFile location
                    mediaPlayer.setDataSource(activeAudio.getFileLocation());
                } catch (IOException e) {
                    e.printStackTrace();
                    stopSelf();
                }
                mediaPlayer.prepareAsync();
            }
        
            public void playMedia() {
                if (!mediaPlayer.isPlaying()) {
                    mediaPlayer.start();
                }
                buildNotification(PlaybackStatus.PLAYING);
                Intent intent = new Intent("PLAYBACK_STARTED");
                LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
            }
        
            private void stopMedia() {
                if (mediaPlayer == null) return;
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.stop();
                }
            }
        
            public void pauseMedia() {
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    resumePosition = mediaPlayer.getCurrentPosition();
                }
                buildNotification(PlaybackStatus.PAUSED);
                Intent intent = new Intent("PLAYBACK_PAUSED");
                LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
            }
        
            private void resumeMedia() {
                if (!mediaPlayer.isPlaying()) {
                    mediaPlayer.seekTo(resumePosition);
                    mediaPlayer.start();
                    buildNotification(PlaybackStatus.PLAYING);
                    Intent intent = new Intent("PLAYBACK_STARTED");
                    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
                }
            }
        
            /**
             * ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs
             */
            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 PhoneState changes
             */
            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);
            }
                
            /**
             * MediaSession and Notification actions
             */
            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();
                    }
        
                    @Override
                    public void onPause() {
                        super.onPause();
                        pauseMedia();
                    }
                });
            }
                
            private void updateMetaData() {
                fetchBitmapOfAlbum();
                // Update the current metadata
                mediaSession.setMetadata(new MediaMetadataCompat.Builder()
                        .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArtBitmap)
                        .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "")
                        .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, activeAudio.getAlbumName())
                        .putString(MediaMetadataCompat.METADATA_KEY_TITLE, activeAudio.getTrackName())
                        .build());
            }
        
            private Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    albumArtBitmap = bitmap;
                }
        
                @Override
                public void onBitmapFailed(Exception e, Drawable errorDrawable) {
        
                }
        
                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };
        
            private void fetchBitmapOfAlbum() {
                Picasso.get().load(activeAudio.getAlbumArt())
                        .placeholder(R.drawable.rotate_animation)
                        .error(R.drawable.ic_blank)
                        .into(target);
            }
            
            private 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);
                }
        
                    fetchBitmapOfAlbum(); //replace with your own image
                    String channelId = "";
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        channelId = "APP_MUSIC";
                    }
                    // Create a new Notification
                    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId)
                            // Hide the timestamp
                            .setShowWhen(false)
                            // Set the Notification style
                            .setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
                                    // Attach our MediaSession token
                                    .setMediaSession(mediaSession.getSessionToken())
                                    // Show our playback controls in the compat view
                                    .setShowActionsInCompactView(0, 1, 2))
                            // Set the Notification color
                            .setColor(ContextCompat.getColor(this.getApplicationContext(), R.color.colorAccent))
                            // Set the large and small icons
                            .setLargeIcon(albumArtBitmap)
                            .setSmallIcon(R.drawable.ic_stat_notifications)
                            // Set Notification content information
                            .setContentText(activeAudio.getTrackName())
                            .setTicker(activeAudio.getAlbumName() + "-" + activeAudio.getTrackName())
                            .setOngoing(true)
                            .setContentTitle(activeAudio.getAlbumName())
                            .setContentInfo(activeAudio.getTrackName())
                            // Add playback actions
                            .addAction(notificationAction, "pause", play_pauseAction)
            
                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notificationBuilder.build());
            }
                
                
            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);
                    default:
                        break;
                }
                return null;
            }
        
            private void removeNotification() {
                NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.cancel(NOTIFICATION_ID);
            }
        
            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();
                } 
            }        
            /**
             * Play new Audio
             */
            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);
            }
            
            public boolean isPlaying(){
                return mediaPlayer.isPlaying();
            }
        
            public void setShuffle(){
                if(shuffle) shuffle=false;
                else shuffle=true;
                StorageUtil storage = new StorageUtil(getApplicationContext());
                storage.storeShuffleRepeat("Shuffle", shuffle);
            }
        
            public void setRepeat(){
                if(repeat) repeat=false;
                else repeat=true;
                StorageUtil storage = new StorageUtil(getApplicationContext());
                storage.storeShuffleRepeat("Repeat", repeat);
            }
        }
    
    1. Add the service to your manifest
    <service android:name=".service.MediaPlayerService" />
    
    1. Bind the service in MainActivity and declare methods to call the service
    public class MainActivity {
        private MediaPlayerService player;
        boolean serviceBound = false;
        public static final String Broadcast_PLAY_NEW_AUDIO = "pkg_name.PlayNewAudio";
        //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;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                serviceBound = false;
            }
        };
    
        // Call this method to play track
        public void playAudio(int audioIndex, ArrayList<PlayableTrack> updatedList) {
            //Check is service is active
            audioList = updatedList;
            if (!serviceBound) {    
                Intent playerIntent = new Intent(this, MediaPlayerService.class);
                startService(playerIntent);
                bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
            } else {   
                //Service is active
                //Send a broadcast to the service -> PLAY_NEW_AUDIO
                Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
                sendBroadcast(broadcastIntent);
            }
    
        }
        // Additional methods for control
        public void start() {
            player.playMedia();
        }
        public void pause() {
            player.pauseMedia();
        }
        public boolean isPlaying() {
            if (player != null && serviceBound) {
                return player.isPlaying();
            }
            return false;
        }
    }
    
    0 讨论(0)
  • 2020-12-07 01:47
    public class serv extends Service{
    
        MediaPlayer mp;
        @Override
        public IBinder onBind(Intent intent) {
            // TODO Auto-generated method stub
            return null;
        }
        public void onCreate()
        {   
            mp = MediaPlayer.create(this, R.raw.b);
            mp.setLooping(false);
        }
        public void onDestroy()
        {       
            mp.stop();
        }
        public void onStart(Intent intent,int startid){
    
            Log.d(tag, "On start");
            mp.start();
        }
    }
    
    0 讨论(0)
  • 2020-12-07 01:53

    You could put the music player in a service. This would make it independent from the Activities and you would still be able to control the playback through intents.

    Here are some code example about it: https://stackoverflow.com/a/8209975/2804473 The code below is written by Synxmax here at StackOverflow, and covered in the link above:

    public class BackgroundSoundService extends Service {
        private static final String TAG = null;
        MediaPlayer player;
        public IBinder onBind(Intent arg0) {
    
            return null;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            player = MediaPlayer.create(this, R.raw.idil);
            player.setLooping(true); // Set looping
            player.setVolume(100,100);
    
        }
        public int onStartCommand(Intent intent, int flags, int startId) {
            player.start();
            return 1;
        }
    
        public void onStart(Intent intent, int startId) {
            // TO DO
        }
        public IBinder onUnBind(Intent arg0) {
            // TO DO Auto-generated method
            return null;
        }
    
        public void onStop() {
    
        }
        public void onPause() {
    
        }
        @Override
        public void onDestroy() {
            player.stop();
            player.release();
        }
    
        @Override
        public void onLowMemory() {
    
        }
    }
    
    0 讨论(0)
  • 2020-12-07 01:54
     @Override
        public void onCreate (){
          super.onCreate();
    
           Player = MediaPlayer.create(this, R.raw.jingle);
           mPlayer.setOnErrorListener(this);
    
           if(mPlayer!= null)
            {
                mPlayer.setLooping(true);
                mPlayer.setVolume(100,100);
            }
    
    
            mPlayer.setOnErrorListener(new OnErrorListener() {
    
          public boolean onError(MediaPlayer mp, int what, int
              extra){
    
                onError(mPlayer, what, extra);
                return true;
            }
              });
        }
    
    0 讨论(0)
  • 2020-12-07 02:04

    The top answer is correct, however you have to add the service to the manifest file.

     <service android:enabled="true" android:name="BackgroundSoundService" />
    
    0 讨论(0)
提交回复
热议问题