一、任务需求
- 利用BroadcastReceiver模拟音乐播放器。
- 该播放器应具有最基本功能:播放、暂停、音乐切换、显示当前播放信息
二、BroadcastReceiver
1.定义:
Broadcast(广播机制) 是一种广泛运用的应用程序之间 传输信息 的机制,而 BroadcastReceiver(广播接收器)
则是用于接收来自系统和应用的广播对并对其进行响应的组件,Android中我们要发送的广播内容是一个Intent,这个Intent中可以携带我们要传送的数据。
2.BroadcastReceiver注册
创建一个广播接收器非常简单,只需要继承 BroadcastReceiver,并重写onReceive()即可
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//在这里写上相关的处理代码,一般来说,不要此添加过多的逻辑或者是进行任何的耗时操作
//因为广播接收器中是不允许开启多线程的,过久的操作就会出现报错
//因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动某个服务
}
}
BroadcastReceiver也是四大组件之一,所以我们也需要对BroadcastReceiver进行注册,不同于其他四大组件,BroadcastReceiver有两种注册方式,分别是静态注册和动态注册。
2.1 静态注册
- 在AndroidManifest中的application标签下添加receiver子标签
- 通过name属性指定注册一个广播类,还有enabled与exported属性,enabled代表是否启用这个广播接收器,exported表示是否允许这个广播接收器接受本程序以外的广播
- 在receiver标签下添加intent-filter标签,设置对应action。action可以是系统定义的系统广播,也可以由开发者自己定义
<!-- 静态注册广播-->
<receiver android:name=".component.broadcastreceiver.MyBroadcastReceiver"
android:exported="tr`在这里插入代码片`ue"
android:enabled="true">
<intent-filter>
<!--用于接收开机完成后由系统发送的广播-->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<!--用于接收用户自己定义的广播-->
<action android:name="com.geekholt.component.broadcastreceiver.customer_action" />
</intent-filter>
</receiver>
2.2 动态注册
- 在相关的activity中new MyBroadcastReceiver()
- new intentFilter,调用其setAction方法,参数中传入相关值的action
- 调用context.registerReceiver方法进行注册,方法的第一个参数为broadcastReceiver对象,第二个则是intentFilter对象
MyBroadcastReceiver recevier = new MyBroadcastReceiver();
intentFilter = new IntentFilter();
//用于接收网络发生变化的广播
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//用于接收用户定义的广播
intentFilter.addAction("com.geekholt.component.broadcastreceiver.customer_action");
registerReceiver(recevier,intentFilter);
3. BroadcastReceiver发送
BroadcastReceiver注册完之后,这个BroadcastReceiver就能够接收响应的广播,下面简单概括一下常用的几种广播方法。
3.1 普通广播(Normal Broadcast)
普通广播是完全异步的,通过context.sendBroadcast()方法发送,消息传递效率比较高,但所有接收器的执行顺序不确定。缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播。
3.2 有序广播(Ordered Broadcast)
有序广播是通过context.sendOrderedBroadcast()方法发送,所有的广播者按照优先级依次执行,广播接收器的优先级通过receiver的intent-filter中的android:priority属性来设置,数值越大优先级越高。当广播接收器接收到广播后,可以使用setResult()方法把结果传递给下一个接收者,通过getResult()方法获取上一个接收者传递过来的结果,并可以通过abortBroadcast()方法丢弃该广播,使该广播不再传递给下一个接收者。
3.3 粘性广播(Sticky Broadcast)
粘性广播通过context.sendStickBroadcast()方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。使用此广播时,需要获得BROADCAST_STICKY权限
由于在Android5.0 & API 21中已经失效,所以不建议使用。
3.4 本地广播(Local Broadcast)
前三种广播都是全局广播,所有应用都可以接收到,这样就带来安全隐患,而本地广播只在进程内传播,可以起到保护数据安全的作用。
其实,本地广播的使用与其十分类似,之前的步骤均是一样的,调用者不同而已,本地广播调用的是LocalBroadcastManager相关方法,全局广播调用的是Context的相关方法,其方法名都是一样的。
//实例化LocalBroadcastManager的实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
//注册本地广播
localBroadcastManager.registerReceiver(mBroadcastReceiver, mIntentFilter);
//注销本地广播
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送本地异步广播
localBroadcastManager.sendBroadcast(intent);
//发送本地同步广播
localBroadcastManager.sendBroadcastSync(intent);
使用本地广播并没有静态注册的方法,因为静态注册主要是为了让程序在未启动的情况下也能收到广播,而发动本地广播的时候,我们的程序已经是启动了,所以,自然是没有静态注册这个方法。
4. 系统广播
Android中内置了多个系统广播,当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作(如开机、网络状态变化、拍照等等)时会自动进行系统广播。
源代码:
MyMusicService:
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 java.io.IOException;
public class MyMusicService extends Service {
MyReceiver serviceReceiver;
AssetManager am;
String[] musics=new String[]{"beautiful.mp3","legendsneverdie.mp3","promise.mp3","wish.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("update",0x14);
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();
}
}
}
MainActivity:
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, nextsong, lastsong;
ActivityReceiver activityReceiver;
public static final String CTL_ACTION =
"org.ssh.action.CTL_ACTION";
public static final String UPDATE_ACTION =
"org.ssh.action.UPDATE_ACTION";
// 定义音乐的播放状态,0x11代表没有播放;0x12代表正在播放;0x13代表暂停
int status = 0x11;
String[] titleStrs = new String[]{"美丽新世界","Legends Never Die","约定","心愿"};
String[] authorStrs = new String[]{"伍佰","英雄联盟","周蕙","四个女生"};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取程序界面界面对应按钮
play = this.findViewById(R.id.play);
stop = this.findViewById(R.id.stop);
nextsong = this.findViewById(R.id.nextsong);
lastsong = this.findViewById(R.id.lastsong);
title = this.findViewById(R.id.title);
author = this.findViewById(R.id.author);
// 为四个按钮的单击事件添加监听器
play.setOnClickListener(this);
stop.setOnClickListener(this);
nextsong.setOnClickListener(this);
lastsong.setOnClickListener(this);
activityReceiver = new ActivityReceiver();
// 创建IntentFilter
IntentFilter filter = new IntentFilter();
// 指定BroadcastReceiver监听的Action
filter.addAction(UPDATE_ACTION);
// 注册BroadcastReceiver
registerReceiver(activityReceiver, filter);
Intent intent = new Intent(this, MyMusicService.class);
// 启动后台Service
startService(intent);
}
// 自定义的BroadcastReceiver,负责监听从Service传回来的广播
public class ActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 获取Intent中的update消息,update代表播放状态
int update = intent.getIntExtra("update", -1);
// 获取Intent中的current消息,current代表当前正在播放的歌曲
int current = intent.getIntExtra("current", -1);
//如果状态为正在播放歌曲或暂停
if (current >= 0 && (update == 0x12 || update == 0x13 || update == 0x14)) {
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.stop);
status = 0x12;
break;
case 0x13:
play.setImageResource(R.drawable.play);
status = 0x13;
break;
case 0x14:
break;
}
}
}
@Override
public void onClick(View source)
{
// 创建Intent
Intent intent = new Intent("org.ssh.action.CTL_ACTION");
switch (source.getId())
{
// 按下播放/暂停按钮
case R.id.play:
intent.putExtra("control", 1);
break;
// 按下停止按钮
case R.id.stop:
intent.putExtra("control", 2);
break;
//按下下一曲按钮
case R.id.nextsong:
intent.putExtra("control",3);
break;
//按下上一曲按钮
case R.id.lastsong:
intent.putExtra("control",4);
break;
}
// 发送广播,将被Service组件中的BroadcastReceiver接收到
sendBroadcast(intent);
}
}