项目概述
- Android Studio 4.1.3
- Gradle 6.5
- JDK 8
- Android 11.0
基本功能:播放和暂停音乐,停止音乐,切换到上一首或者下一首音乐
进阶功能:显示歌曲信息、循环播放和进度条同步
一、布局设计
整体采用线性布局模式,从上至下依次为
- 歌曲信息展示界面
- 歌曲进度显示界面
- 操作按钮界面
二、编写MusicService
- 实现音乐播放的功能(通过MediaPlayer)
- 能够接受广播,根据广播内容来控制播放状态
1.播放音乐
将代码进行封装,达到复用的目的
private void prepareAndPlay(String music)
{
try
{
// 打开指定音乐文件
AssetFileDescriptor afd = am.openFd(music);
mPlayer.reset();
// 使用MediaPlayer加载指定的声音文件。
mPlayer.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
// 准备声音
mPlayer.prepare();
// 播放
mPlayer.start();
}
catch (IOException e)
{
e.printStackTrace();
}
}
2.循环播放
实现MediaPlayer的Completion监听器,监测歌曲播放状态,当歌曲播放结束时自动切换到下一首歌曲
// 创建MediaPlayer
mPlayer = new MediaPlayer();
// 为MediaPlayer播放完成事件绑定监听器
mPlayer.setOnCompletionListener(mp -> {
Log.d("MusicService", "歌曲播放完毕");
current++;
if (current >= 3)
{
current = 0;
}
//发送广播通知Activity更改文本框
Intent sendIntent = new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("current", current);
// 发送广播,将被Activity组件中的BroadcastReceiver接收到
sendBroadcast(sendIntent);
// 准备并播放音乐
prepareAndPlay(musics[current]);
});
3.广播通信
动态注册接收器广播
@Override
public void onCreate()
{
//其他部分代码
// 创建BroadcastReceiver
serviceReceiver = new MyReceiver();
// 创建IntentFilter
IntentFilter filter = new IntentFilter();
filter.addAction(MainActivity.CTL_ACTION);
registerReceiver(serviceReceiver, filter);
//其余部分代码
}
整体逻辑比较简单,通过广播中control的值来进行switch case控制。
public class MyReceiver extends BroadcastReceiver
{
@Override
public void onReceive(final Context context, Intent intent)
{
// 获取Intent中的Control状态
int control = intent.getIntExtra("control", -1);
Log.d("MyReceiver", "MusicService has received a broadcast and the control is "+control);
switch (control)
{
// 播放或暂停
case 1:
// 原来处于没有播放状态
if (status == 0x11)
{
// 准备并播放音乐
prepareAndPlay(musics[current]);
status = 0x12;
}
// 原来处于播放状态
else if (status == 0x12)
{
// 暂停
mPlayer.pause();
// 改变为暂停状态
status = 0x13;
}
// 原来处于暂停状态
else if (status == 0x13)
{
// 播放
mPlayer.start();
// 改变状态
status = 0x12;
}
break;
//停止声音
case 2:
if (status == 0x12 || status == 0x13) {
mPlayer.stop();
status = 0x11;
}
break;
}
// 广播通知Activity更改图标、文本框
Intent sendIntent = new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("update", status);
sendIntent.putExtra("current", current);
// 发送广播,将被Activity组件中的BroadcastReceiver接收到
sendBroadcast(sendIntent);
}
}
4.进度条
采用线程方式实现,监测歌曲播放状态,然后给主线程发送广播通知其修改相关进度条显示,设置线程休眠时长,避免过于频繁地刷新界面造成程序卡顿
@Override
public void onCreate()
{
//其余部分代码
// 刷新进度条
processThread = new Thread(() -> {
while (true) {
// 如果是播放状态
if (status == 0x12) {
try {
// 休眠一秒
Thread.sleep(1000);
// 给主线程发送广播
Intent sendIntent1 = new Intent(MainActivity.UPDATE_ACTION);
// 当前进度
sendIntent1.putExtra("currentTime", mPlayer.getCurrentPosition());
// 总进度
sendIntent1.putExtra("totalTime", mPlayer.getDuration());
// 发送广播,将被Activity组件中的BroadcastReceiver接收到
sendBroadcast(sendIntent1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
processThread.start();
}
三、编写MainActivity
定义两个行为分别用于MainActivity和MusicService监听的动作
static final String CTL_ACTION = "org.zzw.action.CTL_ACTION";
static final String UPDATE_ACTION = "org.zzw.action.UPDATE_ACTION";
动态注册Receiver
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 创建IntentFilter
IntentFilter filter = new IntentFilter();
// 指定BroadcastReceiver监听的Action
filter.addAction(UPDATE_ACTION);
// 注册BroadcastReceiver
registerReceiver(activityReceiver, filter);
// 启动后台Service
startService(new Intent(this, MusicService.class));
}
自定义广播接收器,负责监听Service回传的广播
// 自定义的BroadcastReceiver,负责监听从Service传回来的广播
public class ActivityReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
// 获取Intent中携带的各种信息
//设置歌曲信息
//设置歌曲时长
//设置进度条和当前进度
//根据update值进行选择控制
switch (update)
{
case 0x11:
play.setImageResource(R.drawable.play);
status = 0x11;
break;
// 控制系统进入播放状态
case 0x12:
// 播放状态下设置使用暂停图标
play.setImageResource(R.drawable.pause);
// 设置当前状态
status = 0x12;
break;
// 控制系统进入暂停状态
case 0x13:
// 暂停状态下设置使用播放图标
play.setImageResource(R.drawable.play);
// 设置当前状态
status = 0x13;
break;
}
}
}