最近有一个需求,App音频播放需要将字幕同步到车机上,并且支持基础的暂停/播放、上一首、下一首等操作。
首先我们需要用到MediaSession。当然如果想要更好的兼容,可以使用MediaSessionCompat。
具体差别如下:
MediaSession 和 MediaSessionCompat 都是 Android 中用于管理媒体会话的类,它们在功能上有相似之处,但也存在一些关键区别,以下为你详细介绍:
1. 兼容性
MediaSession:它是 Android 5.0(API 级别 21)引入的原生类,只能在 Android 5.0 及以上版本使用。如果你的应用需要支持低于 Android 5.0 的设备,直接使用 MediaSession 会导致兼容性问题。
MediaSessionCompat:是 Android Support Library(现在的 AndroidX)提供的兼容性类,它为旧版本的 Android 系统提供了对 MediaSession 功能的支持。通过使用 MediaSessionCompat,你可以在较低版本的 Android 设备上实现与 MediaSession 类似的功能,增强了应用的兼容性。
2. 使用场景
MediaSession:当你的应用只需要支持 Android 5.0 及以上版本,并且不需要考虑兼容性问题时,可以直接使用 MediaSession。它提供了原生的媒体会话管理功能,与系统的集成更加紧密。
MediaSessionCompat:如果你的应用需要支持更广泛的 Android 版本,特别是需要兼容 Android 5.0 以下的设备,建议使用 MediaSessionCompat。它封装了 MediaSession 的功能,并提供了统一的 API 接口,方便开发者在不同版本的 Android 系统上进行开发。
3. API 差异
基本功能相似:MediaSessionCompat 提供了与 MediaSession 类似的 API 接口,包括创建媒体会话、设置回调、设置元数据、处理播放状态等功能。开发者可以使用相似的代码逻辑来实现媒体会话的管理。
细微差异:由于 MediaSessionCompat 是为了兼容旧版本系统而设计的,它可能会在某些 API 的使用方式上与 MediaSession 存在细微差异。例如,在处理某些高级功能时,MediaSession 可能提供了更丰富的选项,而 MediaSessionCompat 可能会进行一些简化或封装。
我选择使用MediaSessionCompat,这不重要,因为功能都是一致的。
讲一下我遇到的问题
1、字幕没有正常显示;
2、每次暂停过后,车机上的时间进度重置到0秒,并且只能前进1秒;
3、无论作何努力,图片就是不显示。
原因
1、MediaMetadata.METADATA_KEY_TITLE 时而做标题,时而做字幕;
2、每次都需要设置正确的时长;
3、图片必须是宽高相等的,且不能太大。
然后直接贴上我的代码
public class FZMediaSession {
private static final String TAG = "FZMediaSession";
private static FZMediaSession mInstance;
private MediaSessionCompat mMediaSession;
private MediaSessionCompat.Callback mMediaSessionCallback;
private String mRecentSrt;
private MediaMetadataCompat.Builder builder;
private FZMediaSession() {
builder = new MediaMetadataCompat.Builder();
}
public static FZMediaSession getInstance() {
if (mInstance == null) {
synchronized (FZMediaSession.class) {
mInstance = new FZMediaSession();
}
}
return mInstance;
}
public void changePlayState(boolean isPlaying, long durationMs, long positionMs, float speed) {
initSession();
displayDuration(durationMs);
FZLogger.d(TAG, "isPlaying:" + isPlaying + ",positionMs: " + positionMs);
int playState = isPlaying ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(playState, positionMs, speed)
.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build());
}
public void displayDuration(long durationMs) {
builder.putLong(MediaMetadata.METADATA_KEY_DURATION, durationMs);
executeMetadata(builder.build());
}
public void displayAlbum(String title, String artList) {
FZLogger.d(TAG, "displayAlbum:title:" + title + ", album:" + artList);
initSession();
builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);//标题
builder.putString(MediaMetadata.METADATA_KEY_ALBUM, title);//标题
if (TextUtils.isEmpty(artList)) {
builder.putString(MediaMetadata.METADATA_KEY_ARTIST, IShowDubbingApplication.getInstance().getString(R.string.qpy_app_name));//作者或者专辑名称
} else {
builder.putString(MediaMetadata.METADATA_KEY_ARTIST, artList);//作者或者专辑名称
}
if (!mMediaSession.isActive()) {
builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);//标题
} else {
builder.putString(MediaMetadata.METADATA_KEY_ALBUM, title);//标题
}
executeMetadata(builder.build());
// builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, title);//标题
// builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, title);//标题
// builder.putString(MediaMetadata.METADATA_KEY_AUTHOR, "Author");//标题
// executeMetadata(builder.build(), false);
// if (!mMediaSession.isActive()) {
// mMediaSession.setActive(true);
// }
}
public void displaySrt(String srt) {
if (TextUtils.isEmpty(srt)) {
return;
}
if (srt.equals(mRecentSrt)) {
return;
}
mRecentSrt = srt;
FZLogger.d(TAG, "displaySrt:" + srt);
builder.putString(MediaMetadata.METADATA_KEY_TITLE, srt);//字幕
executeMetadata(builder.build());
}
public void displayCover(String coverUrl) {
FZLogger.d(TAG, "displayCover:" + coverUrl);
if (TextUtils.isEmpty(coverUrl)) {
builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null);
builder.putBitmap(MediaMetadata.METADATA_KEY_ART, null);
executeMetadata(builder.build());
} else {
String savePath = FileFactory.COMMON.getFilePath(FileFactory.getFileName(coverUrl), true);
if (FZUtils.isExist(savePath)) {
displayCoverByLocalPath(savePath);
} else {
downloadCoverPic(coverUrl, savePath);
}
}
}
private void displayCoverByLocalPath(String localPath) {
if (!FZUtils.isExist(localPath)) {
return;
}
Uri fileUri = FileProvider.getUriForFile(IShowDubbingApplication.getInstance().getBaseContext(),
BuildConfig.APPLICATION_ID + ".fileProvider", new File(localPath));
if (fileUri != null) {
FZLogger.d(TAG, "displayCoverByLocalPath:" + fileUri);
Bitmap bitmap = centerCropBitmap(BitmapFactory.decodeFile(localPath), 200);
builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap);
builder.putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap);
// builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, fileUri.toString());
executeMetadata(builder.build());
}
}
private void initSession() {
if (mMediaSession == null) {
mMediaSession = new MediaSessionCompat(IShowDubbingApplication.getInstance().getApplicationContext(), "remusic");
}
if (mMediaSessionCallback == null) {
mMediaSessionCallback = new MediaSessionCompat.Callback() {
@Override
public void onPause() {
super.onPause();
FZFmAudioPlayer.getInstance().pause();
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
try {
FZFmAudioPlayer.getInstance().playNext();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
try {
FZFmAudioPlayer.getInstance().playPrevious();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPlay() {
super.onPlay();
FZFmAudioPlayer.getInstance().resume();
}
};
mMediaSession.setCallback(mMediaSessionCallback);
}
}
private void executeMetadata(MediaMetadataCompat metadataCompat) {
initSession();
if (!mMediaSession.isActive()) {
mMediaSession.setActive(true);
}
mMediaSession.setMetadata(metadataCompat);
}
public void release() {
// if (mMediaSession != null) {
// mMediaSession.release();
// mMediaSession = null;
// }
// mMediaSessionCallback = null;
}
public Bitmap centerCropBitmap(Bitmap originalBitmap, int n) {
int width = originalBitmap.getWidth();
int height = originalBitmap.getHeight();
int left = 0;
int top = 0;
int right = width;
int bottom = height;
// 计算裁剪的起始位置
if (width > height) {
left = (width - height) / 2;
right = left + height;
} else if (height > width) {
top = (height - width) / 2;
bottom = top + width;
}
// 先裁剪成正方形
Bitmap squareBitmap = Bitmap.createBitmap(originalBitmap, left, top, right - left, bottom - top);
// 再将正方形缩放成 n x n 的大小
return Bitmap.createScaledBitmap(squareBitmap, n, n, true);
}
private void downloadCoverPic(String url, String savePath) {
FZFileDownloader.getInstance().getFileDownloadTaskBuilder()
.withDownloadUrl(url)
.withSavePath(savePath)
.withListener(new DownloadListener() {
@Override
public void onConnectIng() {
}
@Override
public void onGetSize(long totalSize) {
}
@Override
public void onDownloadIng(float progressF, int progress) {
}
@Override
public void onCancel() {
}
@Override
public void onError(String error) {
}
@Override
public void onRepeatTask() {
}
@Override
public void onDone(String localPath) {
FZLogger.d(TAG, "onDone, localPath == " + localPath);
displayCoverByLocalPath(localPath);
}
}).build().start();
}
}