Android实战 – 音心播放器 (Music Service 实现)
最后更新于:2022-04-01 10:52:42
## 1.背景
音乐的播放,为了实现在后台播放,将在Service 中进行音乐的基本控制,所以 MediaPlay 将在Service 中实现。
在这里使用了,简单的 startService 方式,非 Ibind 方式操作服务,故在关闭Service 上还有些bug , 但并不影响使用;
*Service 学习参考资料 : [http://www.android-doc.com/guide/components/services.html](http://www.android-doc.com/guide/components/services.html)*
*Service 生命周期 : [http://blog.csdn.net/LABLENET/article/details/48073093](http://blog.csdn.net/LABLENET/article/details/48073093)*
在这个播放器中,为了播放音乐方便,故在应用启动后,当数据加载成功就启动了MusicService 服务。后台服务本身是不可见的,但是在这个为了使其可见就使用了通知(MusicNotification )来人性话,显示服务当前的状态和当前播放音乐的信息,通知下篇实现,这里的主角是Service.
MusicService 服务 :
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764bc7a84.jpg)
## 2.Service 实现
(1)实现MusicService
如果直接看源码的时候,十分头疼这些是什么东西,我在这个简单的说明的一下 :
MusicService 实现的功能是对音乐的基本控制:
1.应用启动 (随着应用的启动而启动) 就启动
2.状态栏显示 : notification
3.注册 : bordcastReceiver
4.请求第1首歌 : 更新状态栏
5.实现上一曲,下一曲,播放,暂停控制
6.初始化 MusicActivity 内容数据 (这个是歌词/进度条显示页面)
*MusicService 源码 : 使用了斜杠进行了分类,应该是可以理解的,boradcastReceiver 的内容将在后面说明。*
~~~
package cn.labelnet.framework;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import cn.labelnet.model.MusicModel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.IBinder;
import android.widget.MediaController.MediaPlayerControl;
import android.widget.Toast;
public class MusicService extends Service implements OnPreparedListener,
OnCompletionListener, OnErrorListener {
/**
* MusicService 音乐播放控制 : 随着应用的启动而启动 基本步揍 : 1.应用启动 : 就启动 2.状态栏显示 :
* notification 3.注册 : bordcastReceiver 4.请求第1首歌 : 更新状态栏 5.实现上一曲,下一曲,播放,暂停控制
*/
// 常量
private final String MUSIC_INTENT_KEY = "musics";
private final int MUSIC_INTENT_FLAG = 20001;
private final int MAIN_MUSIC_INTENT_FLAG = 20017;
// 音乐列表
private List<MusicModel> musics = null;
private int mmSize = 0;
// 通知栏
private MusicNotification musicNotifi = null;
private MusicModel mm = null;
// MediaPlay
private MediaPlayer mp = null;
private int currentTime = 0;
// Music广播接收
private MusicBroadCast musicBroadCast = null;
// MainActivity 来的 Action
private final String MAIN_ACTIVIY_ACTION = "mainActivity.To.MusicService";
// 来自通知栏的Action
private final String MUSIC_NOTIFICATION_ACTION_PLAY = "musicnotificaion.To.PLAY";
private final String MUSIC_NOTIFICATION_ACTION_NEXT = "musicnotificaion.To.NEXT";
private final String MUSIC_NOTIFICATION_ACTION_CLOSE = "musicnotificaion.To.CLOSE";
private final String MUSIC_NOTIFICAION_INTENT_KEY = "notify.music";
// MusicService 来的 Action
private final String MUSIC_ACTIVITY_SERVICE_ACTION = "activity.to.musicservice";
private final String MUSIC_ACTIVITY_SERVICE_KEY = "musictype";
private final int MUSIC_ACTIVITY_SERVICE_REQUEST = 40001;
// 给MusicActivity 的 Action
private final String MUSIC_SERVICE_RECEIVER_ACTION = "service.to.musicactivity";
private Intent mActivityIntent = null;
private final String MUSIC_SERVICE_TO_ACTIVITY_MODEL = "model";
private final String MUSIC_SERVICE_TO_ACTIVITY_ISPLAY = "isplay";
private final String MUSIC_SERVICE_TO_ACTIVITY_NOWTIME = "nowtime";
// 响应码 : 41001 没数据 , 41002 : 有数据
private final String MUSIC_SERVICE_TOACTIVITY_CODE = "mpcode";
// Intent keys
private final String MAIN_MUSIC_INTENT_KEY = "mIntent";
@Override
public void onCreate() {
// 初始化MusicActivity 的 Intent ,给 MusicActivity 发送广播 ,修改音乐播放界面
mActivityIntent = new Intent();
mActivityIntent.setAction(MUSIC_SERVICE_RECEIVER_ACTION);
// 初始化通知栏
musicNotifi = MusicNotification.getMusicNotification();
musicNotifi.setContext(getApplicationContext());
musicNotifi
.setManager((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
musicNotifi.onCreateMusicNotifi();
// 初始化MediaPlay : 设置监听事件
mp = new MediaPlayer();
mp.setOnPreparedListener(this);
mp.setOnCompletionListener(this);
mp.setOnErrorListener(this);
// 注册广播
musicBroadCast = new MusicBroadCast();
IntentFilter filter = new IntentFilter();
filter.addAction(MAIN_ACTIVIY_ACTION);
filter.addAction(MUSIC_ACTIVITY_SERVICE_ACTION);
filter.addAction(MUSIC_NOTIFICATION_ACTION_PLAY);
filter.addAction(MUSIC_NOTIFICATION_ACTION_NEXT);
filter.addAction(MUSIC_NOTIFICATION_ACTION_CLOSE);
registerReceiver(musicBroadCast, filter);
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressWarnings("unchecked")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
musics = (List<MusicModel>) intent
.getSerializableExtra(MUSIC_INTENT_KEY);
}catch(Exception e){
}
if (musics != null) {
mmSize = musics.size();
}
// showToast("1." + musics.get(1).getSongname());
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
if (mp != null) {
mp.stop();
mp.release();
mp = null;
musics = null;
}
// 取消注册的广播
unregisterReceiver(musicBroadCast);
}
// //////////////////////////////Music Util//////////////////////
// Toast
private void showToast(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
// 音乐播放
private void play(String musicUrl) {
mp.reset();
try {
mp.setDataSource(getApplicationContext(), Uri.parse(musicUrl));
mp.prepareAsync();
} catch (IOException e) {
showToast("网络错误,播放失败");
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音乐暂停
private void pause() {
if (mp.isPlaying()) {
currentTime = mp.getCurrentPosition();
mp.pause();
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// 音乐继续播放
private void resume() {
mp.start();
if (currentTime > 0) {
mp.seekTo(currentTime);
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音乐停止
private void stop() {
mp.stop();
try {
mp.prepare();
} catch (IOException e) {
showToast("音乐停止异常");
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// //////////////////////////////Music MediaPlayListener////////////
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 出错的时候
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
// 播放完毕的时候
// showToast("播放完毕,准备播放下一首!");
currentTime = 0;
// 改变通知栏
musicNotifi.onUpdataMusicNotifi(mm, false);
//改变MusicActivity
sendModelToMusicActivity();
}
@Override
public void onPrepared(MediaPlayer mp) {
// 准备加载的时候
resume();
sendModelToMusicActivity();
}
// ////////////////////////////其他工具方法//////////////////////////////////
/**
* 发送Model给MusicActivity
*/
private void sendModelToMusicActivity() {
if (mm != null) {
// 正在播放的歌曲实体
if(mm.getSeconds()==0){
mm.setSeconds(mp.getDuration()/1000);
}
mActivityIntent.putExtra(MUSIC_SERVICE_TOACTIVITY_CODE, 41002);
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_ISPLAY,
mp.isPlaying());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_NOWTIME,
mp.getDuration() - mp.getCurrentPosition());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_MODEL,
(Serializable) mm);
} else {
// 默认发送第一首歌信息
mm = musics.get(0);
mActivityIntent.putExtra(MUSIC_SERVICE_TOACTIVITY_CODE, 41001);
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_ISPLAY,
mp.isPlaying());
mActivityIntent.putExtra(MUSIC_SERVICE_TO_ACTIVITY_MODEL,
(Serializable) mm);
}
sendBroadcast(mActivityIntent);
}
// //////////////////////////////Music BroadCastReceiver////////////
// 接收广播
private class MusicBroadCast extends BroadcastReceiver {
private int flag = 0, position = -1, kzhi = 0, musictype = 0;
@Override
public void onReceive(Context context, Intent intent) {
// 2.MainActivity 控制
flag = intent.getFlags();
mainToService(intent);
// 3.MusicNotification控制
kzhi = intent.getIntExtra("type", -1);
if (kzhi > 0) {
musicNotificationService(kzhi);
}
// 4.MusicActivity 来的控制
musictype = intent.getIntExtra(MUSIC_ACTIVITY_SERVICE_KEY, 0);
if (musictype > 0) {
musicActivityService(musictype);
}
}
/**
* 来自 MusicActivity 的控制
*
* @param musictype2
*/
private void musicActivityService(int musictype2) {
// showToast("musicActivityService 执行了 musictype2 :" + musictype2);
switch (musictype2) {
case 40001:
sendModelToMusicActivity();
break;
case 40002:
//播放与暂停
playSong();
break;
case 40003:
//下一曲
nextSong();
break;
case 40004:
//上一曲
preSong();
break;
}
}
/**
* musicNotification 来的控制
*
* @param intent
*/
private void musicNotificationService(int k) {
switch (k) {
case 30001:
// 播放
playSong();
break;
case 30002:
// 下一首
nextSong();
break;
case 30003:
// 关闭通知栏
musicNotifi.onCancelMusicNotifi();
// 停止音乐
stop();
break;
}
}
/**
* 播放
*/
private void playSong() {
if (mp.isPlaying()) {
pause();
} else {
if (currentTime > 0) {
resume();
} else {
if (mm != null) {
play(mm.getUrl());
}
}
}
sendModelToMusicActivity();
}
/**
* 下一曲
*/
private void nextSong() {
currentTime=0;
if(position<0){
position=0;
}
if (mmSize > 0) {
position++;
if (position < mmSize) {
// 不超过长度
mm = musics.get(position);
play(mm.getUrl());
} else {
// 超过长度 播放第一首
mm = musics.get(0);
play(mm.getUrl());
}
}
}
/**
* 上一曲
*/
private void preSong(){
currentTime=0;
if(position<0){
position=0;
}
if (mmSize > 0) {
position--;
if (position>=0) {
//不小于0
mm = musics.get(position);
play(mm.getUrl());
} else {
// 超过长度 播放第一首,小于0 ,播放第一首
mm = musics.get(0);
play(mm.getUrl());
}
}
}
/**
* MainActivity来的数据
*
* @param intent
*/
private void mainToService(Intent intent) {
if (MAIN_MUSIC_INTENT_FLAG == flag) {
// 来自MainActivity 的操作
position = intent.getIntExtra(MAIN_MUSIC_INTENT_KEY, -1);
// showToast("3.来自MainActivity 问候 : " + position);
if (position > -1) {
// 播放
if (musics != null) {
mm = musics.get(position);
} else {
// showToast("4.MUSICS IS NULL");
}
if (mm != null) {
/**
* 1.播放音乐 2.更新状态栏 3.如果进度条运行的话,通知改变
*/
play(mm.getUrl());
} else {
// showToast("5.musics 数据去哪里了!");
}
} else {
// showToast("6.这怎么可能发生呢?!");
}
} else {
// showToast("不是MainActivity 来的数据");
}
}
}
}
~~~
(2)注册Service
在AndroidMainfest.xml中注册
~~~
<service android:name="cn.labelnet.framework.MusicService" >
</service>
~~~
## (3)启动服务
这里使用了 Serializable序列化进行了数据传递,不建议使用这个,效率低,可以使用Parcleable,效率高;
参考文章 :
[Android - Parcelable接口用法 和 与 Serializable 的区别](http://blog.csdn.net/lablenet/article/details/50315349)
~~~
@Override
public void getMusicModelList(List<MusicModel> models) {
// 初始化 Service : 开启MUSIC服务
Intent intent = new Intent(MainActivity.this, MusicService.class);
intent.putExtra(MUSIC_INTENT_KEY, (Serializable) models);
intent.addFlags(MUSIC_INTENT_FLAG);
startService(intent);
//关闭进度条
swiperefresh_wei.setRefreshing(false);
isRefresh=false;
}
~~~
### 3.播放器控制
播放器控制主要实现是实现 play() , pause() , resume(),stop() 四个方法,进行播放,暂停,重新播放控制。同时实现onPrepaed() , onCompletion() , onError() 三个监听,实现对播放器的控制,最基本的操作就在此了。后面的通知栏控制,歌词/进度条页面控制 均是使用 这几个方法进行控制实现。
~~~
<span style="font-family:Comic Sans MS;font-size:18px;">// 音乐播放
private void play(String musicUrl) {
mp.reset();
try {
mp.setDataSource(getApplicationContext(), Uri.parse(musicUrl));
mp.prepareAsync();
} catch (IOException e) {
showToast("网络错误,播放失败");
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音乐暂停
private void pause() {
if (mp.isPlaying()) {
currentTime = mp.getCurrentPosition();
mp.pause();
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// 音乐继续播放
private void resume() {
mp.start();
if (currentTime > 0) {
mp.seekTo(currentTime);
}
musicNotifi.onUpdataMusicNotifi(mm, true);
}
// 音乐停止
private void stop() {
mp.stop();
try {
mp.prepare();
} catch (IOException e) {
showToast("音乐停止异常");
}
musicNotifi.onUpdataMusicNotifi(mm, false);
}
// //////////////////////////////Music MediaPlayListener////////////
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 出错的时候
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
// 播放完毕的时候
// showToast("播放完毕,准备播放下一首!");
currentTime = 0;
// 改变通知栏
musicNotifi.onUpdataMusicNotifi(mm, false);
//改变MusicActivity
sendModelToMusicActivity();
}
@Override
public void onPrepared(MediaPlayer mp) {
// 准备加载的时候
resume();
sendModelToMusicActivity();
}</span>
~~~
### 4.总结
MusicService 的实现是整个播放器的核心,所以进步的音乐控制均在此实现,算是播放器的心脏。
下篇将实现通知栏实现- MusicSerivce 的脸;