本章小节主要讲述音频播放器的service相关内容。由于代码属于公司资产个人无权外发,而且代码牵扯的范围广泛也难以全部贴上来,所以只讲重点的细节实现和小段代码。
首先服务播放的权限不可少
ini
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
1、先列举服务中的变量
arduino
private static AudioPlayerManager apm; //音频播放管理
private static int mPosSomeTime = 0;// 播放指针 单元
private String jsonList, unit_name; //歌曲列表数据 歌曲名称
private static int bookId; //内层专辑ID
private static String book_name; //内层专辑ID
private static long process = 0; //当前歌曲播放进度
private static long max = 0; //当前歌曲最大进度
private static ExecutorService fixedThreadPool;
private static int isFrom; //来源 0 专辑列表 1首页 2 AI问答
private String book_cover; //专辑封面
private static int bizId; //外层专辑ID
private static String bizTopic; //外层专辑主题
private static long timeStart = 0; //开始计时的时间戳
private static boolean countDowning = false; //是否倒计时中
2、初始化中 设置播放监听、eventbus注册、线程池等,其中播放结束监听是关键,很多播放逻辑在此触发。
scss
@Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
fixedThreadPool = Executors.newFixedThreadPool(3);
apm = AudioPlayerManager.getInstance(TbjcPlayerService.this);
Player.Listener listener = new Player.Listener() {
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case Player.STATE_BUFFERING://加载缓存且还未准备好时触发
break;
case Player.STATE_READY: //播放准备
timeStart = System.currentTimeMillis(); //记录播放开始时间
break;
case Player.STATE_ENDED: //播放结束时触发
process=0;
fixedThreadPool.execute(command4); //保存播放进度
int sum = 0;
sum = ConstantFd.unit_List.size(); //专辑中的歌曲数量
if(ConstantFd.loop_type == 2 && sum > 0){ //随机播放
int index = (int) (Math. random() * sum-1);
to_play(index);
}else if(ConstantFd.loop_type == 0){ //顺序播放
Log.e("TAG", "mPosSomeTime---> "+mPosSomeTime);
if(mPosSomeTime < sum - 1){//播放指针 小于 列表总数
play_next(1); //播放下一个
}else { //已播完最后一首 暂停操作
ConstantFd.isplay = false;
apm.playOrPause(false);
PlayStateMsg.sendState(false); //发送播放状态 通知ui
}
}else if(ConstantFd.loop_type == 1){ //单课循环
Log.e("TAG", "单课循环---> 重新开始");
playUrl = ConstantFd.unit_List.get(mPosSomeTime).getPlayUrl();
apm.setAudioFile2(playUrl); //在线
apm.start();
}
break;
}
}
};
apm.setlistener(listener); //设置播放监听
apm.switchSpeed(ConstantFd.speed); //设置播放倍速
//设置网络监听
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(networkReceiver, filter, Context.RECEIVER_EXPORTED);
}else {
registerReceiver(networkReceiver, filter);
}
}
3、首次数据加载 变量赋值、开始播放、倒计时方法
ini
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// NotificationUtil.startForeground(TbjcPlayerService.this, "播放器正在运行", "点按即可了解详情或停止应用");
if (intent != null) {
book_cover = intent.getStringExtra("book_cover");
book_name = intent.getStringExtra("book_name");
bookId = intent.getIntExtra("bookId", 0);
jsonList = intent.getStringExtra("jsonList");
isFrom = intent.getIntExtra("isFrom", 0);
bizId = intent.getIntExtra("bizId", 0);
unit_name = intent.getStringExtra("unit_name");
bizTopic = intent.getStringExtra("bizTopic");
Log.i("TAG", "jsonList : " + jsonList);
}else {
Log.e("TAG", "intent null---> ");
}
apm.requestAudioFocus(); //请求音频播放焦点
if(!TextUtils.isEmpty(jsonList)){ //解析列表数据
unit_List = new Gson().fromJson(jsonList, new TypeToken<List<ReadDataBean>>() { }.getType());
startPlay(); //开始播放
}else {
reLoad();
}
if (!countDowning) {
SharedPreferences sharedPreferences = getSharedPreferences(ConstantFd.PREFERENCES, MODE_PRIVATE); //上次定时关闭
int time = sharedPreferences.getInt(ConstantFd.LastTimeSet, 0);
if(time > 0){
showSurplusTime = true; //显示倒计时
countDown(time * 60 * 1000);
}else {
showSurplusTime = false;
}
}else {
ConstantFd.showSurplusTime = true;
if (ConstantFd.surplusTime > 0) {
mHandler.removeMessages(2);
mHandler.sendEmptyMessageDelayed(2, 1000);
}
}
return START_NOT_STICKY;
}
private void reLoad(){ //加载播放
isplay = false; //播放状态设为false
if (!TextUtils.isEmpty(book_name) && bookId >= 0) {
fixedThreadPool.execute(command1); //使用线程池避免播就了之后卡顿
}
}
private void countDown(long time) { //设置时间 开始到计时
Log.i("TAG", "ConstantFd.surplusTime: "+ConstantFd.surplusTime);
ConstantFd.surplusTime = time;
ConstantFd.setTime = time;
mHandler.removeMessages(2);
if (ConstantFd.surplusTime > 0) {
countDowning = true;
mHandler.sendEmptyMessageDelayed(2, 1000);
} else {
countDowning = false;
}
}
4、设置播放的相关方法
scss
//开始播放
private void startPlay(){
PlayInfoMsg.sendInfo(true); //发送消息 通知UI刷新列表和进度条
mPosSomeTime = getPoi(unit_name);
if(apm.isPlaying() && isFrom==1){ //如果是播放中 继续播放,做到从首页跳转过来无缝衔接播放
PlayShowPoiMsg.sendPoi(mPosSomeTime);
PlayStateMsg.sendState(true);
}else {
if(!TextUtils.isEmpty(unit_name)){ //从列表页传来的单元名称
PlayUnitPoiMsg.sendPoi(mPosSomeTime, book_cover);
}else {
fixedThreadPool.execute(command3); //获取上次播放的单元指针
}
// apm.playOrPause(ConstantFd.isplay);
}
Log.i("TAG", "111 isplay: "+ConstantFd.isplay);
Log.i("TAG", "111 control_play: "+ConstantFd.control_play);
if(!ConstantFd.control_play){ //外面手动暂停 进播放页暂停
PlayControlTools.setACTION_PAUSE();
}
}
private static void setPlayPoi(long poi){ //播放当前歌曲中的指定位置
// Log.i("TAG", "滑动的 poi: "+poi);
apm.getMediaPlayer().seekTo(poi);
}
private void play_next(int LeftorRight) { //播放下一首 0上一个 1下一个
if(!ConstantFd.hasList()){ //列表无数据
return;
}
int position = mPosSomeTime; //当前单元位置
if (position == -1) {
return;
}
int sum = 0;
sum = ConstantFd.unit_List.size();
if (LeftorRight == 0) { //左
if (position > 0) {
position--;
} else {
position = sum - 1;
}
} else if (LeftorRight == 1) { //右
if (position < sum - 1) {
position++;
} else {
position = 0;
}
}
to_play(position);
}
private void to_play(int position) { //播放列表中的哪一首歌
listenUpload();
mPosSomeTime = position;
PlayUnitPoiMsg.sendPoi(mPosSomeTime, book_cover); //发送播放指针 并播放
}
5、服务中的消息通信 接收来自ui的设置或操作
scss
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveTime(PlayTimeSetMsg msg) { //接收设置的定时时间
countDown(msg.getTime());
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveProcess(PoiToPlayMsg msg) { //接收滑动的进度
setPlayPoi(msg.getPoi());
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveSet(PlaySetMsg msg) { //播放设置倍速
apm.switchSpeed(ConstantFd.speed);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveEventBus_Poi(PlayUnitPoiMsg msg) { //接收指针 播这一首
mPosSomeTime = msg.getPoi();
ConstantFd.isplay = true;
PlayStateMsg.sendState(true);
playStart();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void receiveEventBus(PlayControlMsg msg) { //接收消息控制播放
if (msg.getMsg().equals(PlayControlMsg.YYTS_ACTION_PAUSE)) { //暂停
playOrPause(false);
PlayStateMsg.sendState(false); //发送播放状态 通知ui
} else if (msg.getMsg().equals(PlayControlMsg.YYTS_ACTION_PLAY)) { //播放
if(apm.getMediaPlayer().getCurrentPosition() >= max){
setPlayPoi(0);
}
playOrPause(true);
PlayStateMsg.sendState(true); //发送播放状态 通知ui
} else if (msg.getMsg().equals(PlayControlMsg.YYTS_PLAY_NEXT)) { //下一首
int sum = 0;
sum = ConstantFd.unit_List.size();
if (ConstantFd.loop_type == 2 && sum > 0) { //随机播放
int index = (int) (Math.random() * sum - 1);
to_play(index);//播放指定位置歌曲
} else {
play_next(1);//播放下一首
}
} else if (msg.getMsg().equals(PlayControlMsg.YYTS_PLAY_PREVIOUS)) { //上一首
int sum = 0;
sum = ConstantFd.unit_List.size();
if (ConstantFd.loop_type == 2 && sum > 0) { //随机播放
int index = (int) (Math.random() * sum - 1);
to_play(index); //播放指定位置歌曲
} else {
play_next(0); //播放上一首
}
}
}
/**
* 播放时间设置
*/
public class PlayTimeSetMsg {
long time;
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public static void sendMsg(long time){
PlayTimeSetMsg msg = new PlayTimeSetMsg();
msg.setTime(time);
EventBus.getDefault().post(msg); //发送状态
}
}
6、服务中的播放线程
scss
private static Handler mHandler = new Handler(new Handler.Callback() { //static 定时
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0: //获取当前进度并发给ui
max = apm.getMediaPlayer().getDuration();
process = apm.getMediaPlayer().getCurrentPosition();
if(max<0){
max=0;
}
// Log.i("TAG", "process: "+process);
// Log.i("TAG", "max: "+max);
PoiToUIMsg.sendMsg(process, max); //发送进度到UI
if(ConstantFd.isplay){
mHandler.sendEmptyMessageDelayed(0, 1000);
}
fixedThreadPool.execute(command4); //保存播放进度
break;
case 1:
setPlayPoi(msg.arg1);//设置上次进度
break;
case 2: //定时播放 倒计时
ConstantFd.surplusTime = ConstantFd.surplusTime - 1000;
if (ConstantFd.showSurplusTime) {
PlayTimeShowMsg.sendMsg(ConstantFd.surplusTime);
}
// Log.i("TAG", "ConstantFd.surplusTime: "+ConstantFd.surplusTime);
if (ConstantFd.surplusTime > 0) {
if(ConstantFd.isplay){
mHandler.sendEmptyMessageDelayed(2, 1000); //每秒刷新
}
} else { //定时播放结束
ConstantFd.surplusTime = 0;
countDowning = false;
if (ConstantFd.setTime != 0) {
ConstantFd.isplay = false;
apm.playOrPause(false);
PlayStateMsg.sendState(false); //发送播放状态 通知ui
}
}
break;
}
return true;
}
});
private Runnable command1 = new Runnable() {
@Override
public void run() { //请求数据 播放列表
RequestMsg.sendInfo(true);
getDataList(); //获取播放数据
//获取数据成功后要设置播放状态为true 调用开始播放方法
}
};
private Runnable command2 = new Runnable() { //获取上次进度 并保存本次单元指针
@Override
public void run() {
if(!ConstantFd.hasList() || isFrom==2){
return;
}
//获取上次播放进度
Message msg = new Message();
msg.what = 1;
msg.arg1 = (int) getSongProcess();
// Log.i("TAG", "获取上次播放进度 process: "+process);
mHandler.sendMessage(msg);
//保存播放歌曲指针
JxwRoom.getInstance().getJxwDatabase().getLastSongDao().deleteAllInfo();
LastSong song = new LastSong();
song.setSong_name(ConstantFd.unit_List.get(mPosSomeTime).getShowName());
song.setIndex(mPosSomeTime);
song.setBook_id(bookId);
song.setBook_name(book_name);
song.setSong_cover(book_cover);
song.setBiz_id(bizId);
JxwRoom.getInstance().getJxwDatabase().getLastSongDao().insertInfo(song);
// Log.i("TAG", "保存歌曲指针 poi: "+mPosSomeTime);
AlbumSong as =JxwRoom.getInstance().getJxwDatabase().getAlbumSongDao().getSongPoi(book_name, bookId); //保存专辑播放到哪一曲
if(as!=null){
as.setSong_name(ConstantFd.unit_List.get(mPosSomeTime).getShowName());
as.setIndex(mPosSomeTime);
JxwRoom.getInstance().getJxwDatabase().getAlbumSongDao().updateInfo(as);
}else {
as = new AlbumSong();
as.setSong_name(ConstantFd.unit_List.get(mPosSomeTime).getShowName());
as.setIndex(mPosSomeTime);
as.setBook_id(bookId);
as.setBook_name(book_name);
as.setSong_cover(book_cover);
as.setBiz_id(bizId);
JxwRoom.getInstance().getJxwDatabase().getAlbumSongDao().insertInfo(as);
}
}
};
private Runnable command3 = new Runnable() {
@Override
public void run() { //获取上次播的歌 指针
mPosSomeTime = 0;
AlbumSong as =JxwRoom.getInstance().getJxwDatabase().getAlbumSongDao().getSongPoi(book_name, bookId); //保存专辑播放到哪一曲
if(as!=null){
mPosSomeTime = as.getIndex();
}
PlayUnitPoiMsg.sendPoi(mPosSomeTime, book_cover); //发送播放指针
}
};
private static Runnable command4 = new Runnable() {
@Override
public void run() { //保存歌曲播放进度
if(!ConstantFd.hasList() || isFrom==2){
return;
}
if(process >= max){//进度判断
process = 0;
}
if(mPosSomeTime >= ConstantFd.unit_List.size()){
return;
}
//使用数据库保存播放信息
String song_name = ConstantFd.unit_List.get(mPosSomeTime).getShowName();
SongProcess sp = JxwRoom.getInstance().getJxwDatabase().getSongProcessDao().getProcess(book_name, bookId, song_name);
if(sp!=null){
sp.setProcess(process);
JxwRoom.getInstance().getJxwDatabase().getSongProcessDao().updateInfo(sp);
}else {
sp = new SongProcess();
sp.setProcess(process);
sp.setBook_id(bookId);
sp.setBook_name(book_name);
sp.setSong_name(song_name);
sp.setIndex(mPosSomeTime);
JxwRoom.getInstance().getJxwDatabase().getSongProcessDao().insertInfo(sp);
}
}
};
7、每首歌曲播放开始的方法和结束服务
scss
private void playStart() {
String playUrl = "";
if(!ConstantFd.isLocalPlay) { //有网
if (ConstantFd.unit_List.size() > 0 && mPosSomeTime < ConstantFd.unit_List.size() && ConstantFd.unit_List.get(mPosSomeTime) != null) {
playUrl = ConstantFd.unit_List.get(mPosSomeTime).getPlayUrl(); //在线
}
}
if(!TextUtils.isEmpty(playUrl)){
apm.setAudioFile2(playUrl); //设置播放连接
apm.start();
fixedThreadPool.execute(command2); //获取上次播放进度
mHandler.removeMessages(0);
mHandler.sendEmptyMessageDelayed(0, 800);//开始播放
}
}
private int getPoi(String name){ //获取位置 也可以用ID作为判断
int poi = 0;
int sum = 0;
if(ConstantFd.isLocalPlay){ //无网本地
sum = ConstantFd.local_unit_List.size();
}else {
sum = ConstantFd.unit_List.size();
}
for (int i = 0; i < sum; i++) {
if(ConstantFd.isLocalPlay){
if(ConstantFd.local_unit_List.get(i).getSound_name().equals(name)){
return i;
}
}else {
if(ConstantFd.unit_List.get(i).getShowName().equals(name)){
return i;
}
}
}
return poi;
}
@Override
public void onDestroy() {
super.onDestroy();
PlayControlTools.setACTION_PAUSE(); //暂停播放
ConstantFd.control_play = false; //设置状态
ConstantFd.isplay = false; //设置状态
// stopForeground(true);
// 关闭线程池,避免线程泄漏
if (fixedThreadPool != null && !fixedThreadPool.isShutdown()) {
fixedThreadPool.shutdownNow(); // 立即关闭所有任务
fixedThreadPool = null;
}
// 移除所有当前 Service 订阅的粘性事件类型
EventBus.getDefault().removeStickyEvent(ListenUploadMsg.class);
EventBus.getDefault().removeStickyEvent(PlayTimeSetMsg.class);
EventBus.getDefault().removeStickyEvent(PoiToPlayMsg.class);
EventBus.getDefault().removeStickyEvent(PlaySetMsg.class);
EventBus.getDefault().removeStickyEvent(PlayUnitPoiMsg.class);
EventBus.getDefault().removeStickyEvent(PlayControlMsg.class);
// 最后注销订阅
EventBus.getDefault().unregister(this);
if (apm != null) {
apm.setlistener(null); // 移除监听器,切断引用链
apm.release(); // 释放音频资源
apm = null; // 置空,避免后续引用
}
ConstantFd.unit_List.clear(); //清空数据
ConstantFd.local_unit_List.clear();
unregisterReceiver(networkReceiver);
}
以上就是在线播放器的服务中的主要逻辑代码,可供参考