在Andriod Studio中使用Broadcast实现了简易音乐播放器的功能。可以进行播放、暂停、歌曲切换的功能。效果如下图:
由于无法上传视频,故展示GIF,没有音乐播放
已发布至gitee
新建assets文件夹,用于存放音乐资源文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
<ImageButton
android:id="@+id/imgstop"
android:layout_width="125dp"
android:layout_height="124dp"
android:backgroundTint="@android:color/transparent"
android:scaleType="centerInside"
android:src="@drawable/stop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imgplay"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/txttitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未播放歌曲"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imglast"
app:layout_constraintVertical_bias="0.619"
tools:layout_editor_absoluteX="16dp" />
<TextView
android:id="@+id/txtauthor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未播放歌曲"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.767"
app:layout_constraintStart_toEndOf="@+id/txttitle"
app:layout_constraintTop_toBottomOf="@+id/imgnext"
app:layout_constraintVertical_bias="0.609" />
<ImageView
android:id="@+id/imageView"
android:layout_width="198dp"
android:layout_height="219dp"
android:layout_marginTop="40dp"
android:src="@drawable/music"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.46"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/imgplay"
android:layout_width="137dp"
android:layout_height="129dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="15dp"
android:backgroundTint="@android:color/transparent"
android:scaleType="centerInside"
android:src="@drawable/play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/imgnext"
app:layout_constraintStart_toEndOf="@+id/imglast"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/imglast"
android:layout_width="98dp"
android:layout_height="104dp"
android:backgroundTint="@android:color/transparent"
android:scaleType="centerInside"
android:src="@drawable/last"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.494" />
<ImageButton
android:id="@+id/imgnext"
android:layout_width="99dp"
android:layout_height="114dp"
android:backgroundTint="@android:color/transparent"
android:scaleType="centerInside"
android:src="@drawable/next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.49" />
</androidx.constraintlayout.widget.ConstraintLayout>
新建一个service类,用于音乐的播放和状态的改变
package com.example.musicplayer1;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.MediaPlayer;
import android.os.IBinder;
import androidx.annotation.Nullable;
import java.io.IOException;
public class MusicService extends Service {
@Nullable
MyReceiver serviceReceiver;
AssetManager am;
String[] musics=new String[]{"music0.mp3","music1.mp3","music2.mp3","music3.mp3"};
MediaPlayer mPlayer;
//0x11表示没有播放,0x12代表正在播放,0x13代表暂停
int status=0x11;
int current=0;
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate(){
super.onCreate();
am=getAssets();
//创建BroadcastReceiver
serviceReceiver=new MyReceiver();
//创建IntentFilter
IntentFilter filter=new IntentFilter();
filter.addAction(MainActivity.CTL_ACTION);
registerReceiver(serviceReceiver,filter);
mPlayer=new MediaPlayer();
//为MediaPlayer播放完成事件绑定监听器
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
current++;
if (current>=4)
{
current=0;
}
Intent sendIntent = new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("current",current);
//发送广播,将被Activity组件中的BroadcastReceiver接收
sendBroadcast(sendIntent);
//准备播放音乐
prepareAndPlay(musics[current]);
}
});
}
public class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
int control =intent.getIntExtra("control",-1);
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;
case 3:
//原来处于没有播放或暂停状态
if (status==0x11||status==0x13)
{
if(current==0) {
current=3;
prepareAndPlay(musics[current]);
}
//准备并播放音乐
else {
current=current-1;
prepareAndPlay(musics[current]);
}
status=0x12;
}
//原来处于播放状态
else if (status==0x12)
{
//上一首//准备并播放音乐
if(current==0) {
current=3;
prepareAndPlay(musics[current]);
}
else {
current=current-1;
prepareAndPlay(musics[current]);
}
}
break;
case 4:
//原来处于没有播放或暂停状态
if (status==0x11||status==0x13)
{
if(current==3) {
current=0;
prepareAndPlay(musics[current]);
} //准备并播放音乐
else {
current=current+1;
prepareAndPlay(musics[current]);
}
status=0x12;
}
else if (status==0x12)
{
if(current==3) {
current=0;
prepareAndPlay(musics[current]);
}
else {
current=current+1;
prepareAndPlay(musics[current]);
}
}
break;
}
//广播通知Activity更改图标、文本框
Intent sendIntent=new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("update",status);
sendIntent.putExtra("current",current);
//发送广播,将被Activity组件中的BroadcastReceiver接收
sendBroadcast(sendIntent);
}
}
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();
}
}
}
package com.example.musicplayer1;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
public class MainActivity extends Activity implements View.OnClickListener {
TextView title, author;
ImageButton play, stop,last,next;
ActivityReceiver activityReceiver;
public static final String CTL_ACTION=
"org.crazyit.action.CTL_ACTION";
public static final String UPDATE_ACTION=
"org.crazyit.action.UPDATE_ACTION";
int status = 0x11;
String[] titleStrs = new String[] {"只对你有感觉","当你","醉赤壁","当爱已成往事"};
String[] authorStrs = new String[] {"林俊杰","林俊杰","林俊杰","林忆莲"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityReceiver = new ActivityReceiver();
IntentFilter filter = new IntentFilter();
//指定BroadCastReceiver监听的action
filter.addAction(UPDATE_ACTION);
registerReceiver(activityReceiver,filter);
Intent intent = new Intent(this,MusicService.class);
startService(intent);
//找到对应控件
play = this.findViewById(R.id.imgplay);
stop = this.findViewById(R.id.imgstop);
next = this.findViewById(R.id.imgnext);
last = this.findViewById(R.id.imglast);
title = this.findViewById(R.id.txttitle);
author = this.findViewById(R.id.txtauthor);
//添加监听
play.setOnClickListener(this);
stop.setOnClickListener(this);
next.setOnClickListener(this);
last.setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent intent = new Intent("org.crazyit.action.CTL_ACTION");
//在主界面按下对应按钮,传递给service对应参数
switch (view.getId())
{
case R.id.imgplay:
intent.putExtra("control",1);
break;
case R.id.imgstop:
intent.putExtra("control",2);
break;
case R.id.imgnext:
intent.putExtra("control",3);
break;
case R.id.imglast:
intent.putExtra("control",4);
break;
}
sendBroadcast(intent);
}
private class ActivityReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//获取来自receive中intent的update消息,代表播放状态
int update = intent.getIntExtra("update",-1);
//获取来自receive中intent的curruent消息,代表正在播放的歌曲
int current = intent.getIntExtra("current",-1);
//如果状态为正在播放歌曲或暂停
if(current>=0&&(update == 0x12||update == 0x13))
{
title.setText(titleStrs[current]);
author.setText(authorStrs[current]);
}
//如果状态为未播放歌曲
else
{
title.setText("未播放歌曲");
author.setText("未播放歌曲");
}
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;
}
}
}
}
Broadcast在Andriod开发中用的十分广泛,其广播的发送与接收的逻辑关系也比较复杂。在此项目中,主界面需将对于imagebutton的onClick事件通过广播传至Service中,并传递对应的播放操作,而Service则需要接收主界面传过来的播放操作变化情况来具体控制音乐的播放情况,再把更新后的播放状态传递给主界面,主界面根据现在的播放状态更改歌曲标题、作者和图标等,实现简易的音乐播放器功能。
在控制音乐根据不同按钮进行不同的播放操作代码编写时,在逻辑上容易出现问题,对于不同的按键,每个都需要考虑三种不同的播放状态。