Я начал разрабатывать приложение для Android, но у меня возникают некоторые трудности с правильным пониманием фоновых служб и уведомлений Android. Здесь я работаю над приложением потоковой передачи музыки и хочу добавить постоянное уведомление, чтобы пользователь мог взаимодействовать (играть stop music) с сервисом, отвечающим за воспроизведение музыки из интернета в фоновом режиме. Я пытался найти пример обработки нажатий кнопок в уведомлении Android, я нашел пример для обработки этих действий в службах переднего плана через onStartCommand, но я использую связыватель в этом проекте. Это хороший способ создания кнопки мультимедиа уведомлений и обработки этих кнопок нажмите в моем случае.
Вот мой код привязки службы потоковой передачи:
StreamingServiceBinder.java
package com.stream.music;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.media.session.MediaButtonReceiver;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import java.io.IOException;
class StreamServiceBinder extends Binder implements ExoPlayer.EventListener {
// Save web audio file url.
private String audioFileUrl = "";
// Check if stream audio.
private boolean streamAudio = true;
private boolean isHls = false;
// Media player that play audio.
public static final String NOTIFICATION_CHANNEL = "1337";
public static final int NOTIFICATION_ID = 1338;
public static final String STREAM_URI = "STREAM_URI";
public static final String STREAM_TITLE = "STREAM_TITLE";
public static final float VOLUME_DUCK = 0.2f;
public static final float VOLUME_NORMAL = 1.0f;
private static final int AUDIO_NO_FOCUS_LOST = -1;
private static final int AUDIO_NO_FOCUS_NO_DUCK = 0;
private static final int AUDIO_NO_FOCUS_CAN_DUCK= 1;
private static final int AUDIO_FOCUSED = 2;
public static final int STATE_PAUSE = 0;
public static final int STATE_PLAY = 1;
private int mCurrentAudioFocusState = AUDIO_NO_FOCUS_NO_DUCK;
private String currentTitle;
public SimpleExoPlayer mExoPlayer;
private MediaSessionCompat mMediaSession;
private MediaControllerCompat mMediaController;
private MediaSource mediaSource;
// 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 playpauseButtonHandler;
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if(playbackState == Player.STATE_READY){
Log.v("PLAYERSTATE", "isPlaying");
Message pauseButton = new Message();
pauseButton.what = STATE_PAUSE;
// Send the message to caller activity's update audio prgressbar Handler object.
playpauseButtonHandler.sendMessage(pauseButton);
}else{
Log.v("PLAYERSTATE", "isPaused");
Message startButton = new Message();
startButton.what = STATE_PLAY;
// Send the message to caller activity's update audio prgressbar Handler object.
playpauseButtonHandler.sendMessage(startButton);
}
}
private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
mCurrentAudioFocusState = AUDIO_FOCUSED;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
mCurrentAudioFocusState = AUDIO_NO_FOCUS_CAN_DUCK;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
mCurrentAudioFocusState = AUDIO_NO_FOCUS_NO_DUCK;
break;
case AudioManager.AUDIOFOCUS_LOSS:
mCurrentAudioFocusState = AUDIO_NO_FOCUS_LOST;
break;
}
if (mExoPlayer != null) configurePlayerState();
}
};
private final IntentFilter mAudioNoisyIntentFilter =
new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BroadcastReceiver mAudioNoisyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Log.v("Broadcast", "Action received");
// Log.v("Broadcast", intent.getAction());
// int mAction = Integer.parseInt(intent.getAction());
// if(mAction == PlaybackState.ACTION_PLAY){
// startAudio();
//
// }
// else{
// stopAudio();
// }
mMediaController.getTransportControls().pause();
}
};
private boolean mAudioNoisyReceiverRegistered = false;
// This is the message signal that inform audio progress updater to update audio progress.
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;
Log.i("BINDER", "audio file set!");
}
public void setNotifTitle(String title){
this.currentTitle = title;
}
public boolean isStreamAudio() {
return streamAudio;
}
public void setStreamAudio(boolean streamAudio) {
this.streamAudio = streamAudio;
}
public void setHls(boolean hlsfile){
this.isHls = hlsfile;
}
public void setPlaypauseButtonHandler(Handler playpauseButtonHandler) {
this.playpauseButtonHandler = playpauseButtonHandler;
}
// Start play audio.
public void startAudio()
{
initAudioPlayer();
if(mExoPlayer!=null) {
mExoPlayer.setPlayWhenReady(true);
NotificationManager mNotificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
assert mNotificationManager != null;
mNotificationManager
.notify(NOTIFICATION_ID, buildNotification(PlaybackStateCompat.ACTION_PAUSE));
}
}
// Pause playing audio.
public void pauseAudio()
{
if(mExoPlayer!=null) {
mExoPlayer.setPlayWhenReady(false);
}
}
// Stop play audio.
public void stopAudio()
{
if(mExoPlayer!=null) {
mExoPlayer.stop();
destroyAudioPlayer();
}
}
public class MediaSessionCallback extends MediaSessionCompat.Callback {
@Override
public void onPlay() {
registerAudioNoisyReceiver();
mExoPlayer.setPlayWhenReady(true);
NotificationManager mNotificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
assert mNotificationManager != null;
mNotificationManager
.notify(NOTIFICATION_ID, buildNotification(PlaybackStateCompat.ACTION_PAUSE));
}
@Override
public void onPause() {
unregisterAudioNoisyReceiver();
mExoPlayer.setPlayWhenReady(false);
NotificationManager mNotificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
assert mNotificationManager != null;
mNotificationManager
.notify(NOTIFICATION_ID, buildNotification(PlaybackStateCompat.ACTION_PLAY));
}
@Override
public void onStop() {
unregisterAudioNoisyReceiver();
mExoPlayer.stop();
mMediaSession.setActive(false);
NotificationManager mNotificationManager
= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
assert mNotificationManager != null;
mNotificationManager
.notify(NOTIFICATION_ID, buildNotification(PlaybackStateCompat.ACTION_PLAY));
// mNotificationManager.cancel(NOTIFICATION_ID);
// stopSelf();
}
}
public void init(){
AudioManager mAudioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
assert mAudioManager != null;
int result = mAudioManager.requestAudioFocus(
mOnAudioFocusChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN
);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// initAudioPlayer();
}
}
// Initialise audio player.
private void initAudioPlayer()
{
try {
if(mExoPlayer != null) {
mExoPlayer.stop();
mExoPlayer.release();
}
Log.i("SERVICE BACK", "Service started");
mCurrentAudioFocusState = AUDIO_FOCUSED;
registerAudioNoisyReceiver();
mMediaSession = new MediaSessionCompat(context, "Pinstream");
mMediaSession.setCallback(new MediaSessionCallback());
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
mMediaSession.setActive(true);
try {
mMediaController = new MediaControllerCompat(
context, mMediaSession.getSessionToken());
} catch (RemoteException e) {
e.printStackTrace();
}
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
mExoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector);
Uri uri = Uri.parse(getAudioFileUrl());
if(isHls){
mediaSource = new HlsMediaSource.Factory(new DefaultHttpDataSourceFactory("Pinstream")).createMediaSource(uri);
// mainHandler, null);
}else {
mediaSource = new ExtractorMediaSource.Factory(
new DefaultHttpDataSourceFactory("Pinstream"))
.createMediaSource(uri);
}
mExoPlayer.addListener(this);
mExoPlayer.prepare(mediaSource);
mExoPlayer.setPlayWhenReady(true);
Log.i("BINDER", "audio is playing");
}catch(Exception ex)
{
ex.printStackTrace();
}
}
// Destroy audio player.
private void destroyAudioPlayer()
{
if(mExoPlayer!=null)
{
mExoPlayer.release();
mExoPlayer = null;
}
}
private void configurePlayerState() {
switch (mCurrentAudioFocusState) {
case AUDIO_NO_FOCUS_CAN_DUCK:
mExoPlayer.setVolume(VOLUME_DUCK);
break;
case AUDIO_NO_FOCUS_NO_DUCK:
mMediaController.getTransportControls().pause();
break;
case AUDIO_NO_FOCUS_LOST:
mMediaController.getTransportControls().stop();
break;
case AUDIO_FOCUSED:
mExoPlayer.setVolume(VOLUME_NORMAL);
break;
}
}
private void registerAudioNoisyReceiver() {
if (!mAudioNoisyReceiverRegistered) {
context.registerReceiver(mAudioNoisyReceiver, mAudioNoisyIntentFilter);
mAudioNoisyReceiverRegistered = true;
}
}
private void unregisterAudioNoisyReceiver() {
if (mAudioNoisyReceiverRegistered) {
context.unregisterReceiver(mAudioNoisyReceiver);
mAudioNoisyReceiverRegistered = false;
}
}
private Notification buildNotification(long action) {
createNotificationChannel();
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL);
builder
.setContentTitle(currentTitle)
.setContentIntent(mMediaController.getSessionActivity())
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setDeleteIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
context, PlaybackStateCompat.ACTION_STOP));
builder
.setSmallIcon(android.R.drawable.ic_media_play)
.setColor(ContextCompat.getColor(context, R.color.colorPrimaryDark));
if (action == PlaybackStateCompat.ACTION_PLAY) {
builder
.addAction(new NotificationCompat.Action(
R.drawable.play_button, "Play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
context, PlaybackStateCompat.ACTION_PLAY)));
}
if (action == PlaybackStateCompat.ACTION_PAUSE) {
builder
.addAction(new NotificationCompat.Action(
R.drawable.pause_button, "Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
context, PlaybackStateCompat.ACTION_PAUSE)));
}
builder
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setShowActionsInCompactView(0)
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
context, PlaybackStateCompat.ACTION_STOP)));
return builder.build();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "stream";
String description = "music stream";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel =
new NotificationChannel("1337", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
assert notificationManager != null;
notificationManager.createNotificationChannel(channel);
}
}
}