
GitHub地址: github.com/lizixian18/…
在日常开发中,如果需要在项目中添加音频播放功能,那是一件非常麻烦的事情. 需要处理的一般事项是音频服务包装,播放器包装,通知栏管理,链接系统媒体中心,音频焦点获取,播放列表维护,各种API方法编写等. 如果进行了改进,IPC也需要将要执行. 可以看出,有很多事情要处理.
MusicLibrary的写法是这样的,其目的是帮助您实现所有与音频有关的事情,以便您可以专注于其他事情.
实施基于IPC的音频服务,减少应用程序内存高峰,并避免OOM. 集成和调用API非常简单,音频功能几乎可以用一句话集成. 提供丰富的API方法以轻松实现各种功能. 将通知栏集成到一句话中,您可以自定义通知栏的控件. 内部集成了两个播放器,即ExoPlayer和MediaPlayer. 默认情况下使用ExoPlayer播放器build,可以随意切换. 还有其他...
为了反映MusicLibrary的实际应用,编写了一个简单的音乐播放器NiceMusic.
GitHub地址: github.com/lizixian18/…
MusicLibrary的基本用法可以参考该项目中的实现.
在NiceMusic中,您可以了解以下内容:
更好的MVP结构封装,与RxJava结合使用,生命周期与Activity绑定在一起,并且Presenter通过注释实例化,而注释则更加分离. 改造框架封装以及如何使用向所有接口添加通用参数和头信息. 其他等等...
放置一些屏幕截图:
不再讨论IPC和AIDL的用法和原理. 如果您听不懂,请亲自咨询信息.
如您所见,PlayControl实际上是连接客户端和服务器的活页夹.
QueueManager是播放列表管理类,用于维护当前的播放列表和当前的音频索引.
播放列表存储在ArrayList中,默认情况下,音频索引为0:

public QueueManager(MetadataUpdateListener listener, PlayMode playMode) {
mPlayingQueue = Collections.synchronizedList(new ArrayList<SongInfo>());
mCurrentIndex = 0;
...
}
复制代码
在调用与设置播放列表有关的API时,实际上会调用setCurrentQueue方法. 每次清除播放列表时,便会分配值:
public void setCurrentQueue(List<SongInfo> newQueue, int currentIndex) {
int index = 0;
if (currentIndex != -1) {
index = currentIndex;
}
mCurrentIndex = Math.max(index, 0);
mPlayingQueue.clear();
mPlayingQueue.addAll(newQueue);
//通知播放列表更新了
List<MediaSessionCompat.QueueItem> queueItems = QueueHelper.getQueueItems(mPlayingQueue);
if (mListener != null) {
mListener.onQueueUpdated(queueItems, mPlayingQueue);
}
}
复制代码
更新播放列表时,它将把播放列表封装到QueueItem列表中,并在锁定屏幕时回调MediaSessionManager进行与媒体相关的操作.
获取当前正在播放的音乐,播放指定的音乐等实际上是在操作音频索引mCurrentIndex,然后根据该索引提取列表中的相应音频信息.
上一个和下一个操作实际上称为skipQueuePosition方法,该方法使用剩余算法计算上一首和下一首歌曲的索引,
代替加或减一个,这样的好处是可以避免数组越界或计算更加方便:
public boolean skipQueuePosition(int amount) {
int index = mCurrentIndex + amount;
if (index < 0) {
// 在第一首歌曲之前向后跳,让你在第一首歌曲上
index = 0;
} else {
//当在最后一首歌时点下一首将返回第一首个
index %= mPlayingQueue.size();
}
if (!QueueHelper.isIndexPlayable(index, mPlayingQueue)) {
return false;
}
mCurrentIndex = index;
return true;
}
复制代码
参数数量是尺寸的含义. 如您所见,如果通过1,则将获得下一首歌曲;如果通过-1,则将获得下一首歌曲. 实际上,您可以获得任何音频,
只要尺寸不同.
播放音乐时,首先调用setCurrentQueueIndex方法设置音频索引,然后将回调传递给PlaybackManager进行真正的播放处理.
private void setCurrentQueueIndex(int index, boolean isJustPlay, boolean isSwitchMusic) {
if (index >= 0 && index < mPlayingQueue.size()) {
mCurrentIndex = index;
if (mListener != null) {
mListener.onCurrentQueueIndexUpdated(mCurrentIndex, isJustPlay, isSwitchMusic);
}
}
}
复制代码
这是QueueManager需要解释的. 如果您有兴趣,可以克隆代码并仔细查看.
PlaybackManager是一个播放管理类,负责操作播放,暂停和其他播放控制操作.

它实现了Playback.Callback接口,而Playback是定义播放器相关操作的接口.
特定的播放器ExoPlayer和MediaPlayer实现都实现了Playback接口,而PlaybackManager是通过Playback
以统一的方式管理播放器的相关操作. 因此,如果要添加其他播放器,只需实现Playback接口.
播放:
public void handlePlayRequest() {
SongInfo currentMusic = mQueueManager.getCurrentMusic();
if (currentMusic != null) {
String mediaId = currentMusic.getSongId();
boolean mediaHasChanged = !TextUtils.equals(mediaId, mCurrentMediaId);
if (mediaHasChanged) {
mCurrentMediaId = mediaId;
notifyPlaybackSwitch(currentMusic);
}
//播放
mPlayback.play(currentMusic);
//更新媒体信息
mQueueManager.updateMetadata();
updatePlaybackState(null);
}
}
复制代码
播放方法分几个步骤:
删除要播放的音频信息. 根据音频id的比较,判断是否回切歌曲的方法. 如果id不同,则表示歌曲需要剪切. 然后调用mPlayback.play(currentMusic)并将其提供给特定的播放器. 然后更新媒体操作(即屏幕锁定时的播放器)的音频信息. 回调播放状态.
暂停:
public void handlePauseRequest() {
if (mPlayback.isPlaying()) {
mPlayback.pause();
updatePlaybackState(null);
}
}
复制代码
暂停将直接移交给特定播放器以暂停,然后回放状态会被回调.
停止:
public void handleStopRequest(String withError) {
mPlayback.stop(true);
updatePlaybackState(withError);
}
复制代码
停止也是如此.
基本上,PlaybackManager中的操作围绕这三种方法,而其他方法则是一些封装和回调处理.

具体的播放器实现是指Google的官方示例android-UniversalMusicPlayer. 这个项目真的很好.
该类主要用于管理媒体信息MediaSessionCompat,他的写作是相对固定的,可以参考本文链接系统媒体中心的介绍
您还可以参考Google的官方示例
此类封装了通知栏的相关操作. 自定义通知栏的情况可以认为非常复杂,远不止是新的通知. (也许我是菜鸟)
NotificationCompat.Builder中有两种setContentView方法,一种是setCustomContentView()
一个是setCustomBigContentView(). 您可以知道差异就是大小差异. 还有两个RemoteView: RemoteView和BigRemoteView
不同的手机,某些通知栏背景为白色,某些通知栏背景为透明或黑色(例如魅族,小米等),那么您需要根据不同背景显示不同的样式(除非您在布局中显示为死背景)颜色,但这确实很难看)
通知栏需要四种布局:
在白色背景下的ContentView在黑色背景下的BigContentView在黑色背景下的ContentView在黑色背景下的ContentView
按如下所示设置ContentView:
...
if (Build.VERSION.SDK_INT >= 24) {
notificationBuilder.setCustomContentView(mRemoteView);
if (mBigRemoteView != null) {
notificationBuilder.setCustomBigContentView(mBigRemoteView);
}
}
...
Notification notification;
if (Build.VERSION.SDK_INT >= 16) {
notification = notificationBuilder.build();
} else {
notification = notificationBuilder.getNotification();
}
if (Build.VERSION.SDK_INT < 24) {
notification.contentView = mRemoteView;
if (Build.VERSION.SDK_INT >= 16 && mBigRemoteView != null) {
notification.bigContentView = mBigRemoteView;
}
}
...
复制代码
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-238684-1.html
如果再配合降【】17%至7%