BroadcastReceiver:廣播接收者都伪,Android四大組件之一,這個(gè)組件本質(zhì)上就是一個(gè)全局監(jiān)聽(tīng)器,用于監(jiān)聽(tīng)系統(tǒng)全局的廣播消息。由于BroadcastReceiver是一個(gè)全局監(jiān)聽(tīng)器虑椎,因此它可以方便的實(shí)現(xiàn)系統(tǒng)中不同組件之間的通信。
BroadcastReceiver簡(jiǎn)介
BroadcastReceiver用于接收程序(開(kāi)發(fā)者開(kāi)發(fā)的程序和系統(tǒng)程序)發(fā)出的Broadcast Intent俱笛,程序啟動(dòng)BroadcastReceiver需要兩步:
- 創(chuàng)建需要啟動(dòng)的BroadcastReceiver的Intent捆姜。
- 調(diào)用Context的sendBroadcast()或sendOrderedBroadcast()方法來(lái)啟動(dòng)指定的BroadcastReceiver。
實(shí)現(xiàn)BroadcastReceiver只要重寫B(tài)roadcastReceiver的onReceive(Context context, Intent intent)方法即可迎膜。
實(shí)現(xiàn)了BroadcastReceiver泥技,接著應(yīng)該指定該BroadcastReceiver能匹配的Intent,有兩種方式:
-
靜態(tài)注冊(cè):
- 在AndroidManifest.xml中配置:
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="com.trampcr.musicplayer.PLAY_ACTION"/> </intent-filter> </receiver>
-
動(dòng)態(tài)注冊(cè):
- 使用代碼進(jìn)行指定磕仅,調(diào)用BroadcastReceiver的Context的registerReceiver(BroadcastReceiver receiver, IntentFilter filter)方法指定:
IntentFilter intentFilter = new IntentFilter("com.trampcr.musicplayer.PLAY_ACTION"); MyReceiver receiver = new MyReceiver(); registerReceiver(receiver, intentFilter);
每次系統(tǒng)廣播事件發(fā)生后珊豹,系統(tǒng)會(huì)創(chuàng)建對(duì)應(yīng)的BroadcastReceiver實(shí)例,并自動(dòng)觸發(fā)它的onReceiver()方法榕订,如果onReceiver()方法不能在10秒內(nèi)完成店茶,Android就會(huì)認(rèn)為該程序無(wú)響應(yīng)(所以onReceiver()方法中不能進(jìn)行耗時(shí)操作)。onReceiver()方法執(zhí)行完后劫恒,BroadcastReceiver實(shí)例就會(huì)被銷毀贩幻。
如果需要根據(jù)Broadcast完成比較耗時(shí)的操作,則應(yīng)該考慮通過(guò)Intent啟動(dòng)一個(gè)Service來(lái)完成两嘴,不考慮使用新線程完成耗時(shí)操作的原因:
BroadcastReceiver本身的生命周期很短丛楚,很可能子線程還沒(méi)有結(jié)束,BroadcastReceiver就已經(jīng)退出了憔辫。
發(fā)送廣播
調(diào)用Context的sendBroadcast(Intent intent)方法發(fā)送廣播趣些,這條廣播將會(huì)啟動(dòng)intent參數(shù)所對(duì)應(yīng)的BroadcastReceiver。
該程序的Activity界面包含一個(gè)按鈕螺垢,用于向外發(fā)送廣播喧务。代碼如下:
public class MainActivity extends AppCompatActivity {
private Button mSendBroadcast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSendBroadcast = (Button) findViewById(R.id.send_broadcast);
mSendBroadcast.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
//設(shè)置Intent的Action屬性
intent.setAction("com.trampcr.musicplayer.PLAY_ACTION");
intent.putExtra("msg", "simple message");
//發(fā)送廣播
sendBroadcast(intent);
}
});
}
}
上述程序用于創(chuàng)建一個(gè)Intent對(duì)象赖歌,并使用該Intent對(duì)象對(duì)外發(fā)送了一條廣播。
發(fā)送了廣播功茴,就得接收廣播庐冯,接收廣播代碼如下:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String[] msg = intent.getStringArrayExtra("msg");
Toast.makeText(context, "接收的Intent的Action為:" + action + "\n消息內(nèi)容是" + msg, Toast.LENGTH_SHORT).show();
}
}
當(dāng)符合該MyReceiver的廣播出現(xiàn)時(shí),MyReceiver的onReceiver()方法就會(huì)被觸發(fā)坎穿,從而在該方法中顯示廣播所攜帶的消息展父。
發(fā)送廣播時(shí)Intent的Action為com.trampcr.musicplayer.PLAY_ACTION,這就需要配置MyReceiver應(yīng)監(jiān)聽(tīng)Action為該字符串的Intent玲昧,在AndroidManifest.xml中配置:
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.trampcr.musicplayer.PLAY_ACTION"/>
</intent-filter>
</receiver>
點(diǎn)擊發(fā)送廣播按鈕栖茉,可以看到收到廣播的提示,如下:
廣播類型
廣播分為兩種:
- Normal Broadcast(普通廣播):完全異步孵延,可以在同一時(shí)刻被所有接收者接收到吕漂。
- sendBroadcast():發(fā)送Normal Broadcast。
- Ordered Broadcast(有序廣播):接收者按預(yù)先聲明的優(yōu)先級(jí)依次接收Broadcast尘应。
- 優(yōu)先級(jí)聲明在<intent-filter.../>元素的android:priority屬性中惶凝,數(shù)越大優(yōu)先級(jí)越高。
- Ordered Broadcast接收者可以調(diào)用abortBroadcast()方法終止Broadcast Intent的傳播犬钢,一旦終止苍鲜,后面的接收者就無(wú)法接收到Broadcast。
- sendOrderedBroadcast():發(fā)送Ordered Broadcast玷犹。
上面發(fā)送廣播中舉了一個(gè)發(fā)送普通廣播的例子混滔,這里再舉一個(gè)發(fā)送有序分廣播的例子:
該程序的Activity界面只有一個(gè)按鈕,用于發(fā)送一條有序廣播歹颓,代碼如下:
public class MainActivity extends AppCompatActivity {
private Button mSendBroadcast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSendBroadcast = (Button) findViewById(R.id.send_broadcast);
mSendBroadcast.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
//設(shè)置Intent的Action屬性
intent.setAction("com.trampcr.musicplayer.PLAY_ACTION");
intent.putExtra("msg", "simple message");
//發(fā)送有序廣播
sendOrderedBroadcast(intent, null);
}
});
}
}
代碼中指定了Intent的Action屬性坯屿,再調(diào)用sendOrderedBroadcast()方法來(lái)發(fā)送有序廣播。對(duì)于有序廣播晴股,它會(huì)按優(yōu)先級(jí)依次觸發(fā)每個(gè)BroadcastReceiver的onReceiver()方法愿伴。
第一個(gè)BroadcastReceiver代碼:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String msg = intent.getStringExtra("msg");
Toast.makeText(context, "接收的Intent的Action為:" + action + "\n消息內(nèi)容是" + msg, Toast.LENGTH_SHORT).show();
//創(chuàng)建一個(gè)Bundle對(duì)象,并存入數(shù)據(jù)
Bundle bundle = new Bundle();
bundle.putString("first", "第一個(gè)BroadcastReceiver存入的消息");
//將bundle放入結(jié)果中
setResultExtras(bundle);
//取消Broadcast的繼續(xù)傳播
//abortBroadcast();
}
}
MyReceiver不僅處理了它所接收的消息电湘,而且向處理結(jié)果中存入了key為first的消息隔节,這個(gè)消息將可以被第二個(gè)BroadcastReceiver解析出來(lái)。
abortBroadcast()用于取消廣播寂呛,如果這條代碼生效怎诫,那么優(yōu)先級(jí)比MyReceiver低的BroadcastReceiver都將不會(huì)被觸發(fā)。
在AndroidManifest.xml中部署該BroadcastReceiver贷痪,并指定其優(yōu)先級(jí)為20幻妓,代碼如下:
<receiver android:name=".MyReceiver">
<intent-filter android:priority="20">
<action android:name="com.trampcr.musicplayer.PLAY_ACTION"/>
</intent-filter>
</receiver>
接下來(lái)提供第二個(gè)BroadcastReceiver,將會(huì)解析前一個(gè)BroadcastReceiver存入的key為first的消息劫拢,代碼如下:
public class MyReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = getResultExtras(true);
//解析前一個(gè)BroadcastReceiver所存入的key為first的消息
String first = bundle.getString("first");
Toast.makeText(context, "第一個(gè)Broadcast存入的消息為:" + first, Toast.LENGTH_SHORT).show();
}
}
解析出前一個(gè)BroadcastReceiver存入結(jié)果中的key為first的消息肉津。
在AndroidManifest.xml中配置MyReceiver2的優(yōu)先級(jí)為0强胰,如下:
<receiver android:name=".MyReceiver2">
<intent-filter android:priority="0">
<action android:name="com.trampcr.musicplayer.PLAY_ACTION" />
</intent-filter>
</receiver>
先注釋掉abortBroadcast(),點(diǎn)擊發(fā)送有序廣播按鈕妹沙,可以看到先顯示第一個(gè)廣播接收器中的內(nèi)容偶洋,再顯示第二個(gè)廣播接收器中的內(nèi)容,如下:
如果不注釋abortBroadcast()距糖,將會(huì)阻止消息廣播玄窝,消息將傳不到MyReceiver2。
系統(tǒng)廣播
廣播接收器除了可以接收用戶發(fā)送的廣播悍引,還可以接收系統(tǒng)廣播恩脂,常用的系統(tǒng)廣播如下:
- ACTION_TIME_CHANGED:系統(tǒng)時(shí)間被改變。
- ACTION_DATE_CHANGED:系統(tǒng)日期被改變趣斤。
- ACTION_TIMEZONE_CHANGED:系統(tǒng)時(shí)區(qū)被改變俩块。
- ACTION_BOOT_COMPLETED:系統(tǒng)啟動(dòng)完成。
- ACTION_PACKAGE_ADDED:系統(tǒng)添加包唬渗。
- ACTION_PACKAGE_CHANGED:系統(tǒng)的包改變典阵。
- ACTION_PACKAGE_REMOVED:系統(tǒng)的包被刪除。
- ACTION_PACKAGE_RESTARTED:系統(tǒng)的包被重啟镊逝。
- ACTION_PACKAGE_DATA_CLEARED:系統(tǒng)的包數(shù)據(jù)被清空。
- ACTION_BATTERY_CHANGED:電池電量改變嫉鲸。
- ACTION_BATTERY_LOW:電池電量低撑蒜。
- ACTION_POWER_CONNECTED:系統(tǒng)連接電源。
- ACTION_POWER_DISCONNECTED:系統(tǒng)與電源斷開(kāi)玄渗。
- ACTION_SHUTDOWN:系統(tǒng)被關(guān)閉座菠。
基于Service的音樂(lè)播放器
這里開(kāi)發(fā)一個(gè)基于Service的音樂(lè)播放器,音樂(lè)由后臺(tái)運(yùn)行的Service負(fù)責(zé)播放藤树,當(dāng)后臺(tái)的播放狀態(tài)發(fā)生變化時(shí)浴滴,程序?qū)?huì)通過(guò)發(fā)送廣播通知前臺(tái)Activity更新界面;當(dāng)點(diǎn)擊Activity的界面按鈕時(shí)岁钓,系統(tǒng)將通過(guò)發(fā)送廣播通知后臺(tái)Service來(lái)改變播放狀態(tài)升略。
前臺(tái)Activity界面有兩個(gè)按鈕,分別用于控制播放/暫停屡限、停止品嚣,另外還有兩個(gè)文本框,用于顯示正在播放的歌曲名钧大、歌手名翰撑。前臺(tái)Activity的代碼如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private ImageButton mStart;
private ImageButton mStop;
private TextView mMusicName;
private TextView mSongerName;
private ActivityReceiver mActivityReceiver;
public static final String CTL_ACTION = "com.trampcr.action.CTL_ACTION";
public static final String UPDATE_ACTION = "com.trampcr.action.UPDATE_ACTION";
//定義音樂(lè)播放狀態(tài),0x11代表沒(méi)有播放啊央,0x12代表正在播放眶诈,0x13代表暫停
int status = 0x11;
String[] musicNames = new String[]{"完美生活", "那一年", "故鄉(xiāng)"};
String[] songerNames = new String[]{"許巍", "許巍", "許巍"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStart = (ImageButton) findViewById(R.id.start);
mStop = (ImageButton) findViewById(R.id.stop);
mMusicName = (TextView) findViewById(R.id.music_name);
mSongerName = (TextView) findViewById(R.id.songer_name);
mStart.setOnClickListener(this);
mStop.setOnClickListener(this);
mActivityReceiver = new ActivityReceiver();
//創(chuàng)建IntentFilter
IntentFilter filter = new IntentFilter();
//指定BroadcastReceiver監(jiān)聽(tīng)的Action
filter.addAction(UPDATE_ACTION);
//注冊(cè)BroadcastReceiver
registerReceiver(mActivityReceiver, filter);
Intent intent = new Intent(MainActivity.this, MusicService.class);
//啟動(dòng)后臺(tái)Service
startService(intent);
}
public class ActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲取Intent中的update消息涨醋,update代表播放狀態(tài)
int update = intent.getIntExtra("update", -1);
//獲取Intent中的current消息,current代表當(dāng)前正在播放的歌曲
int current = intent.getIntExtra("current", -1);
if (current >= 0){
mMusicName.setText(musicNames[current]);
mSongerName.setText(songerNames[current]);
}
switch (update){
case 0x11:
mStart.setBackgroundResource(R.drawable.play);
status = 0x11;
break;
//控制系統(tǒng)進(jìn)入播放狀態(tài)
case 0x12:
//在播放狀態(tài)下設(shè)置使用暫停圖標(biāo)
mStart.setBackgroundResource(R.drawable.pause);
status = 0x12;
break;
case 0x13:
//在暫停狀態(tài)下設(shè)置使用播放圖標(biāo)
mStart.setBackgroundResource(R.drawable.play);
status = 0x13;
break;
}
}
}
@Override
public void onClick(View v) {
Intent intent = new Intent(CTL_ACTION);
switch (v.getId()){
case R.id.start:
intent.putExtra("control", 1);
break;
case R.id.stop:
intent.putExtra("control", 2);
break;
}
//發(fā)送廣播逝撬,將被Service中的BroadcastReceiver接收到
sendBroadcast(intent);
}
}
ActivityReceiver()用于響應(yīng)后臺(tái)Service所發(fā)出的廣播东帅,該程序?qū)?huì)根據(jù)廣播Intent里的消息來(lái)改變播放狀態(tài),并更新程序界面中按鈕的圖標(biāo)球拦。
onClick中根據(jù)點(diǎn)擊的按鈕發(fā)送廣播靠闭,發(fā)送廣播時(shí)會(huì)把所按下的按鈕標(biāo)識(shí)發(fā)送出來(lái)。
接下來(lái)是后臺(tái)Service坎炼,會(huì)在播放狀態(tài)發(fā)生改變時(shí)對(duì)外發(fā)送廣播愧膀。代碼如下:
public class MusicService extends Service {
MyReceiver serviceReceiver;
AssetManager mAssetManager;
String[] musics = new String[]{"prefectLife.mp3", "thatYear.mp3", "country.mp3"};
MediaPlayer mMediaPlayer;
int status = 0x11;
int current = 0; // 記錄當(dāng)前正在播放的音樂(lè)
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mAssetManager = getAssets();
serviceReceiver = new MyReceiver();
//創(chuàng)建IntentFilter
IntentFilter filter = new IntentFilter();
filter.addAction(MainActivity.CTL_ACTION);
registerReceiver(serviceReceiver, filter);
//創(chuàng)建MediaPlayer
mMediaPlayer = new MediaPlayer();
//為MediaPlayer播放完成事件綁定監(jiān)聽(tīng)器
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
current++;
if (current >= 3) {
current = 0;
}
//發(fā)送廣播通知Activity更改文本框
Intent sendIntent = new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("current", current);
//發(fā)送廣播,將被Activity中的BroadcastReceiver接收到
sendBroadcast(sendIntent);
//準(zhǔn)備并播放音樂(lè)
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: // 播放或暫停
//原來(lái)處于沒(méi)有播放狀態(tài)
if (status ==0x11){
//準(zhǔn)備播放音樂(lè)
prepareAndPlay(musics[current]);
status = 0x12;
}
//原來(lái)處于播放狀態(tài)
else if (status == 0x12){
//暫停
mMediaPlayer.pause();
status = 0x13; // 改變?yōu)闀和顟B(tài)
}
//原來(lái)處于暫停狀態(tài)
else if (status == 0x13){
//播放
mMediaPlayer.start();
status = 0x12; // 改變狀態(tài)
}
break;
//停止聲音
case 2:
//如果原來(lái)正在播放或暫停
if (status == 0x12 || status == 0x13){
//停止播放
mMediaPlayer.stop();
status = 0x11;
}
}
//廣播通知Activity更改圖標(biāo)谣光、文本框
Intent sendIntent = new Intent(MainActivity.UPDATE_ACTION);
sendIntent.putExtra("update", status);
sendIntent.putExtra("current", current);
//發(fā)送廣播檩淋,將被Activity中的BroadcastReceiver接收到
sendBroadcast(sendIntent);
}
}
private void prepareAndPlay(String music) {
try {
//打開(kāi)指定的音樂(lè)文件
AssetFileDescriptor assetFileDescriptor = mAssetManager.openFd(music);
mMediaPlayer.reset();
//使用MediaPlayer加載指定的聲音文件
mMediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
mMediaPlayer.prepare(); // 準(zhǔn)備聲音
mMediaPlayer.start(); // 播放
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyReceiver用于接收前臺(tái)Activity所發(fā)出的廣播,并根據(jù)廣播的消息內(nèi)容改變Service的播放狀態(tài)萄金,當(dāng)播放狀態(tài)改變時(shí)蟀悦,該Service對(duì)外發(fā)送一條廣播,廣播消息將會(huì)被前臺(tái)Activity接收氧敢,前臺(tái)Activity將會(huì)根據(jù)廣播消息更新界面日戈。
為了讓該音樂(lè)播放器能按順序依次播放歌曲,程序?yàn)镸ediaPlayer增加了OnCompletionListener監(jiān)聽(tīng)器孙乖,當(dāng)MediaPlayer播放完成后將自動(dòng)播放下一首歌曲浙炼。
運(yùn)行程序,效果圖如下: