Java Android 媒体播放器(通知)
Java Android Media Player (Notification)
所以我有以下代码并且工作正常,但是我想在用户通知区域中显示媒体控件,以便他们可以在应用程序处于后台时随意播放和停止音乐。
我想知道这是怎么做到的?
代码:
package com.radiomedia.a1liferadio;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private ImageView imagePlayPause;
private TextView textCurrentTime, textTotalDuration;
private SeekBar playerSeekBar;
private MediaPlayer mediaPlayer;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imagePlayPause = findViewById(R.id.imagePlayPause);
textCurrentTime = findViewById(R.id.textCurrentTime);
textTotalDuration = findViewById(R.id.textTotalDuration);
playerSeekBar = findViewById(R.id.playerSeekBar);
mediaPlayer = new MediaPlayer();
playerSeekBar.setMax(100);
imagePlayPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying())
{
handler.removeCallbacks(updater);
mediaPlayer.pause();
imagePlayPause.setImageResource(R.drawable.ic_play);
}else{
mediaPlayer.start();
imagePlayPause.setImageResource(R.drawable.ic_pause);
updateSeekBar();
}
}
});
prepareMediaPlayer();
}
private void prepareMediaPlayer() {
try {
mediaPlayer.setDataSource("http://stream.radiomedia.com.au:8003/stream"); //url of media
mediaPlayer.prepare();
textTotalDuration.setText(milliSecondsToTimer(mediaPlayer.getDuration()));
} catch (Exception exception){
Toast.makeText(this,exception.getMessage(), Toast.LENGTH_SHORT).show();
}
};
private Runnable updater = new Runnable() {
@Override
public void run() {
updateSeekBar();
long currentDuration = mediaPlayer.getCurrentPosition();
textCurrentTime.setText(milliSecondsToTimer(currentDuration));
}
};
private void updateSeekBar(){
if(mediaPlayer.isPlaying()) {
playerSeekBar.setProgress((int) (((float) mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration()) * 100));
handler.postDelayed(updater, 1000);
}
};
private String milliSecondsToTimer(long milliSeconds) {
String timerString = "";
String secondsString;
int hours = (int)(milliSeconds / (1000 * 60 * 60));
int minutes = (int)(milliSeconds % (1000 * 60 * 60)) / (1000 *60);
int seconds = (int)((milliSeconds % (1000 * 60 *60)) % (1000 * 60) / 1000);
if(hours > 0)
{
timerString = hours + ":";
}
if(seconds < 10)
{
secondsString = "0" + seconds;
}else{
secondsString = "" + seconds;
}
timerString = timerString + minutes + ":" + secondsString;
return timerString;
}
}
有两种情况:
第一个
如果你想在后台播放歌曲,你应该启动一个前台服务。前台服务带有通知。(将搜索栏位置、播放暂停状态、播放列表或视频 url 传递给服务)。
使用媒体控件创建自定义通知后,随心所欲
您必须向服务添加媒体播放器,同时将位置、播放暂停状态等参数传递给服务播放器,它会从您之前的 activity 播放器状态播放。
点击通知后,您必须将相同的参数传递给 activity。
第二个
通过附加播放器启动前台服务并将activity绑定到它,并同时处理来自通知和activity的所有控件。
您可以将播放器控件传递给后台服务意义上的通知,以在后台启动和播放歌曲。
您可以使用首选项或 tinydb 在第一种情况下获取要传递的参数以获得最佳性能。
如果你想继续使用 MediaPlayer,你应该遵循这个 old google mediaplayer sample (Use this commit 如果主版本为你崩溃)。
但是如果您不介意使用 ExoPlayer,那么您应该遵循此 new one,因为 ExoPlayer 已经可以处理部分通知内容。
但是在这些示例项目中您可以看到:
- 它不仅仅是一条通知那么简单,它还与人们按下耳机上的播放按钮等有关
- 你真的需要一个服务来绑定通知,处理 action.MEDIA_BUTTON 意图。
使用 MediaSession 添加此类通知和使用 MediaPlayer(因此不是 ExoPlayer)的服务的最少代码如下所示:
将媒体兼容库添加到您的应用程序构建 gradle:
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "androidx.media:media:1.1.0"
}
然后创建一个 class 用于创建通知:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import androidx.media.session.MediaButtonReceiver;
/**
* Keeps track of a notification and updates it automatically for a given MediaSession. This is
* required so that the music service don't get killed during playback.
*/
public class MediaNotificationManager {
public static final int NOTIFICATION_ID = 412;
private static final String TAG = MediaNotificationManager.class.getSimpleName();
private static final String CHANNEL_ID = "com.example.android.musicplayer.channel";
private static final int REQUEST_CODE = 501;
private final MediaSessionService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MediaSessionService musicContext) {
mService = musicContext;
mNotificationManager =
(NotificationManager) mService.getSystemService(Service.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.ic_pause,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
}
public NotificationManager getNotificationManager() {
return mNotificationManager;
}
public Notification getNotification(MediaMetadataCompat metadata,
@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}
private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {
// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
builder.setStyle(
new MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0)
// For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP)))
.setColor(ContextCompat.getColor(mService, R.color.colorPrimary))
.setSmallIcon(R.drawable.ic_play)
// Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent())
// Title - Usually Song name.
.setContentTitle(description.getTitle())
// When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE));
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
return builder;
}
// Does nothing on versions of Android earlier than O.
@RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}
private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
private PendingIntent createContentIntent() {
Intent openUI = new Intent(mService, MainActivity.class);
openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);
}
}
然后创建处理播放的服务 class 并按下通知按钮:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
public class MediaSessionService extends Service {
public MediaPlayer mediaPlayer;
public static final String TAG = "MediaSessionService";
public static final int NOTIFICATION_ID = 888;
private MediaNotificationManager mMediaNotificationManager;
private MediaSessionCompat mediaSession;
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mMediaNotificationManager = new MediaNotificationManager(this);
mediaSession = new MediaSessionCompat(this, "SOME_TAG");
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
mediaPlayer.start();
}
@Override
public void onPause() {
mediaPlayer.pause();
}
});
Notification notification =
mMediaNotificationManager.getNotification(
getMetadata(), getState(), mediaSession.getSessionToken());
startForeground(NOTIFICATION_ID, notification);
}
public MediaMetadataCompat getMetadata() {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist");
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title");
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer.getDuration()
);
return builder.build();
}
private PlaybackStateCompat getState() {
long actions = mediaPlayer.isPlaying() ? PlaybackStateCompat.ACTION_PAUSE : PlaybackStateCompat.ACTION_PLAY;
int state = mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
final PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
stateBuilder.setActions(actions);
stateBuilder.setState(state,
mediaPlayer.getCurrentPosition(),
1.0f,
SystemClock.elapsedRealtime());
return stateBuilder.build();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ("android.intent.action.MEDIA_BUTTON".equals(intent.getAction())) {
KeyEvent keyEvent = (KeyEvent) intent.getExtras().get("android.intent.extra.KEY_EVENT");
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer.pause();
} else {
mediaPlayer.start();
}
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
更新您的清单以声明服务并向其发送 action.MEDIA_BUTTON 意图。
<service
android:name=".MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
pre-OREO 设备需要上述 MediaButtonReceiver,它会将事件转发到这些平台上的服务。
然后从主 activity 启动服务。
ContextCompat.startForegroundService(
MainActivity.this.getApplicationContext(),
new Intent(MainActivity.this.getApplicationContext(), MediaSessionService.class));
这里还需要做的是让你的主要 activity 以某种方式向你的服务或服务中的播放器发送操作,更新通知和服务状态的变化,但你应该能够得到一些东西使用此代码并从 google 示例代码中找出其余部分。
所以我有以下代码并且工作正常,但是我想在用户通知区域中显示媒体控件,以便他们可以在应用程序处于后台时随意播放和停止音乐。
我想知道这是怎么做到的?
代码:
package com.radiomedia.a1liferadio;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private ImageView imagePlayPause;
private TextView textCurrentTime, textTotalDuration;
private SeekBar playerSeekBar;
private MediaPlayer mediaPlayer;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imagePlayPause = findViewById(R.id.imagePlayPause);
textCurrentTime = findViewById(R.id.textCurrentTime);
textTotalDuration = findViewById(R.id.textTotalDuration);
playerSeekBar = findViewById(R.id.playerSeekBar);
mediaPlayer = new MediaPlayer();
playerSeekBar.setMax(100);
imagePlayPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(mediaPlayer.isPlaying())
{
handler.removeCallbacks(updater);
mediaPlayer.pause();
imagePlayPause.setImageResource(R.drawable.ic_play);
}else{
mediaPlayer.start();
imagePlayPause.setImageResource(R.drawable.ic_pause);
updateSeekBar();
}
}
});
prepareMediaPlayer();
}
private void prepareMediaPlayer() {
try {
mediaPlayer.setDataSource("http://stream.radiomedia.com.au:8003/stream"); //url of media
mediaPlayer.prepare();
textTotalDuration.setText(milliSecondsToTimer(mediaPlayer.getDuration()));
} catch (Exception exception){
Toast.makeText(this,exception.getMessage(), Toast.LENGTH_SHORT).show();
}
};
private Runnable updater = new Runnable() {
@Override
public void run() {
updateSeekBar();
long currentDuration = mediaPlayer.getCurrentPosition();
textCurrentTime.setText(milliSecondsToTimer(currentDuration));
}
};
private void updateSeekBar(){
if(mediaPlayer.isPlaying()) {
playerSeekBar.setProgress((int) (((float) mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration()) * 100));
handler.postDelayed(updater, 1000);
}
};
private String milliSecondsToTimer(long milliSeconds) {
String timerString = "";
String secondsString;
int hours = (int)(milliSeconds / (1000 * 60 * 60));
int minutes = (int)(milliSeconds % (1000 * 60 * 60)) / (1000 *60);
int seconds = (int)((milliSeconds % (1000 * 60 *60)) % (1000 * 60) / 1000);
if(hours > 0)
{
timerString = hours + ":";
}
if(seconds < 10)
{
secondsString = "0" + seconds;
}else{
secondsString = "" + seconds;
}
timerString = timerString + minutes + ":" + secondsString;
return timerString;
}
}
有两种情况:
第一个
如果你想在后台播放歌曲,你应该启动一个前台服务。前台服务带有通知。(将搜索栏位置、播放暂停状态、播放列表或视频 url 传递给服务)。
使用媒体控件创建自定义通知后,随心所欲
您必须向服务添加媒体播放器,同时将位置、播放暂停状态等参数传递给服务播放器,它会从您之前的 activity 播放器状态播放。
点击通知后,您必须将相同的参数传递给 activity。
第二个
通过附加播放器启动前台服务并将activity绑定到它,并同时处理来自通知和activity的所有控件。
您可以将播放器控件传递给后台服务意义上的通知,以在后台启动和播放歌曲。
您可以使用首选项或 tinydb 在第一种情况下获取要传递的参数以获得最佳性能。
如果你想继续使用 MediaPlayer,你应该遵循这个 old google mediaplayer sample (Use this commit 如果主版本为你崩溃)。
但是如果您不介意使用 ExoPlayer,那么您应该遵循此 new one,因为 ExoPlayer 已经可以处理部分通知内容。
但是在这些示例项目中您可以看到:
- 它不仅仅是一条通知那么简单,它还与人们按下耳机上的播放按钮等有关
- 你真的需要一个服务来绑定通知,处理 action.MEDIA_BUTTON 意图。
使用 MediaSession 添加此类通知和使用 MediaPlayer(因此不是 ExoPlayer)的服务的最少代码如下所示: 将媒体兼容库添加到您的应用程序构建 gradle:
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "androidx.media:media:1.1.0"
}
然后创建一个 class 用于创建通知:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import androidx.media.session.MediaButtonReceiver;
/**
* Keeps track of a notification and updates it automatically for a given MediaSession. This is
* required so that the music service don't get killed during playback.
*/
public class MediaNotificationManager {
public static final int NOTIFICATION_ID = 412;
private static final String TAG = MediaNotificationManager.class.getSimpleName();
private static final String CHANNEL_ID = "com.example.android.musicplayer.channel";
private static final int REQUEST_CODE = 501;
private final MediaSessionService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MediaSessionService musicContext) {
mService = musicContext;
mNotificationManager =
(NotificationManager) mService.getSystemService(Service.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.ic_pause,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
}
public NotificationManager getNotificationManager() {
return mNotificationManager;
}
public Notification getNotification(MediaMetadataCompat metadata,
@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}
private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {
// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
builder.setStyle(
new MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0)
// For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP)))
.setColor(ContextCompat.getColor(mService, R.color.colorPrimary))
.setSmallIcon(R.drawable.ic_play)
// Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent())
// Title - Usually Song name.
.setContentTitle(description.getTitle())
// When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE));
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
return builder;
}
// Does nothing on versions of Android earlier than O.
@RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}
private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
private PendingIntent createContentIntent() {
Intent openUI = new Intent(mService, MainActivity.class);
openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);
}
}
然后创建处理播放的服务 class 并按下通知按钮:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
public class MediaSessionService extends Service {
public MediaPlayer mediaPlayer;
public static final String TAG = "MediaSessionService";
public static final int NOTIFICATION_ID = 888;
private MediaNotificationManager mMediaNotificationManager;
private MediaSessionCompat mediaSession;
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mMediaNotificationManager = new MediaNotificationManager(this);
mediaSession = new MediaSessionCompat(this, "SOME_TAG");
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
mediaPlayer.start();
}
@Override
public void onPause() {
mediaPlayer.pause();
}
});
Notification notification =
mMediaNotificationManager.getNotification(
getMetadata(), getState(), mediaSession.getSessionToken());
startForeground(NOTIFICATION_ID, notification);
}
public MediaMetadataCompat getMetadata() {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist");
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title");
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer.getDuration()
);
return builder.build();
}
private PlaybackStateCompat getState() {
long actions = mediaPlayer.isPlaying() ? PlaybackStateCompat.ACTION_PAUSE : PlaybackStateCompat.ACTION_PLAY;
int state = mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
final PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
stateBuilder.setActions(actions);
stateBuilder.setState(state,
mediaPlayer.getCurrentPosition(),
1.0f,
SystemClock.elapsedRealtime());
return stateBuilder.build();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ("android.intent.action.MEDIA_BUTTON".equals(intent.getAction())) {
KeyEvent keyEvent = (KeyEvent) intent.getExtras().get("android.intent.extra.KEY_EVENT");
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer.pause();
} else {
mediaPlayer.start();
}
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
更新您的清单以声明服务并向其发送 action.MEDIA_BUTTON 意图。
<service
android:name=".MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
pre-OREO 设备需要上述 MediaButtonReceiver,它会将事件转发到这些平台上的服务。
然后从主 activity 启动服务。
ContextCompat.startForegroundService(
MainActivity.this.getApplicationContext(),
new Intent(MainActivity.this.getApplicationContext(), MediaSessionService.class));
这里还需要做的是让你的主要 activity 以某种方式向你的服务或服务中的播放器发送操作,更新通知和服务状态的变化,但你应该能够得到一些东西使用此代码并从 google 示例代码中找出其余部分。