安卓四大組件之—BroadcastReceiver詳解

一 概述

BroadcastReceiver是安卓四大組件之一,它是一個(gè)全局的監(jiān)聽器,它能夠接收安卓系統(tǒng)、App內(nèi)以及其它App發(fā)出的廣播。安卓的廣播采用觀察者模式,基于消息的 訂閱 / 發(fā)布 事件模型,因此廣播的發(fā)送者和接收者完全解耦,易于使用和擴(kuò)展。

二 BroadcastReceiver使用流程

  1. 自定義BroadcastReceiver
    我們要想使用BroadcastReceiver需要先定義個(gè)類繼承BroadcastReceiver然后實(shí)現(xiàn)其public void onReceive(Context context, Intent intent)方法。

廣播接收器接收到相應(yīng)的廣播后會(huì)自動(dòng)回調(diào)onReceive方法,該方法運(yùn)行在主線程中,因此不宜做耗時(shí)操作。
一般情況下在onReceive方法中會(huì)涉及到與其它組件的交互,比如打開Service、啟動(dòng)Activity等。

代碼示例

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //獲得意圖(intent)中傳遞過(guò)來(lái)的action
        String action = intent.getAction();
        Log.e(getClass().getSimpleName(),"action:"+action);
        //可以在意圖(intent)中使用bundle傳遞參數(shù)
        Bundle extras = intent.getExtras();
        String id = extras.getString("id");
        String name = extras.getString("name");
        Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
    }
}
  1. BroadcastReceiver注冊(cè)
    我們自定義了BroadcastReceiver之后,還需要注冊(cè)一下才能收到對(duì)應(yīng)的廣播消息。廣播的注冊(cè)有兩種方式分別為:靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)。
  • 靜態(tài)注冊(cè)
    靜態(tài)注冊(cè)的方式是直接在xml文件中注冊(cè),示例如下:
<receiver
    //廣播接收者的類名路徑
    android:name=".MyReceiver"
    //發(fā)送廣播的app應(yīng)該具有的權(quán)限
    android:permission="String"
    //廣播是否可用,為true時(shí)系統(tǒng)才會(huì)初始化該廣播
    android:enabled="true"
    //是否能接受其他app發(fā)出的廣播,當(dāng)有intent-filter時(shí)默認(rèn)為true
    android:exported="true">
    <intent-filter>
        //接受到的廣播類型,可以使用系統(tǒng)已有的廣播類型也可以自定義
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

當(dāng)app啟動(dòng)時(shí)系統(tǒng)會(huì)自動(dòng)實(shí)例化該廣播類,并注冊(cè)到系統(tǒng)中

  • 動(dòng)態(tài)注冊(cè)
    動(dòng)態(tài)注冊(cè)就是在代碼中注冊(cè),這種方式比較靈活,當(dāng)用到廣播時(shí)才去注冊(cè)且可以反注冊(cè),廣播不會(huì)一直存在,節(jié)約內(nèi)存??磦€(gè)示例:
@Override
protected void onResume() {
    super.onResume();
    broadcastReceiver = new MyBroadcastReceiver();
    //創(chuàng)建意圖過(guò)濾器并添加Action
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.jrmf360.sendrp");
    //注冊(cè)BroadcastReceiver
    registerReceiver(broadcastReceiver,intentFilter);
}


@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(broadcastReceiver);
}


class MyBroadcastReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.e(getClass().getSimpleName(),"action:"+action);
        Bundle extras = intent.getExtras();
        String id = extras.getString("id");
        String name = extras.getString("name");
        Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
    }
}

在onResume方法中注冊(cè)BroadcastReceiver在onPause方法中取消注冊(cè)BroadcastReceiver。注冊(cè)和反注冊(cè)必須成對(duì)出現(xiàn)以免造成內(nèi)存泄漏。

  1. 廣播種類
    廣播分為普通廣播、有序廣播、系統(tǒng)廣播和App內(nèi)廣播,具體說(shuō)明如下:
  • 普通廣播
    開發(fā)者自定義的廣播,發(fā)送廣播如下:
//創(chuàng)建意圖并設(shè)置Action
Intent intent = new Intent();
//只有當(dāng)廣播的接收Action于此匹配才能接收到該廣播
intent.setAction("com.jrmf360.sendrp");
//使用bundle傳遞參數(shù)
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//發(fā)送廣播
sendBroadcast(intent);
  • 有序廣播
    發(fā)送出去的廣播按照先后順序被廣播接收者接收,有序是針對(duì)廣播接收者而言的。
    接收規(guī)則:
    按照Priority屬性值從大-小排序
    如果Priority屬性值相同動(dòng)態(tài)廣播優(yōu)先
    高優(yōu)先級(jí)的廣播可以更改廣播中的數(shù)據(jù)然后通過(guò)public final void setResult (int code, String data, Bundle extras)方法把更改后的結(jié)果傳遞給下一級(jí)廣播;高優(yōu)先級(jí)的廣播還可以通過(guò)調(diào)用abortBroadcast()方法攔截廣播,后續(xù)所有的廣播接收者都將收不到該廣播。
  • 系統(tǒng)廣播
    安卓系統(tǒng)內(nèi)置了很多系統(tǒng)廣播,只要涉及到手機(jī)的基本操作基本都會(huì)發(fā)出對(duì)應(yīng)的廣播,例如:開關(guān)機(jī)、亮屏以及音量變化等。每個(gè)廣播都有Intent - Filter(包括具體的action),安卓中一些內(nèi)置的Action如下:
系統(tǒng)操作 action
網(wǎng)絡(luò)變化 android.net.conn.CONNECTIVITY_CHANGE
充電時(shí)或電量發(fā)生變化 Intent.ACTION_BATTERY_CHANGED
屏幕被關(guān)閉 Intent.ACTION_SCREEN_OFF
屏幕被打開 Intent.ACTION_SCREEN_ON
系統(tǒng)啟動(dòng)完成后(僅廣播一次) Intent.ACTION_BOOT_COMPLETED
  • 應(yīng)用內(nèi)廣播
    當(dāng)我們使用自定義廣播時(shí)可能會(huì)出現(xiàn)一些問(wèn)題:
    其它App發(fā)出和我們廣播接收者相同的Action導(dǎo)致我們不斷地收到廣播并處理;
    其它App定義和我們相同的廣播接收者導(dǎo)致我們的廣播信息暴露。
    要解決以上問(wèn)題我們就需要使用到應(yīng)用內(nèi)廣播,所謂應(yīng)用內(nèi)廣播保證了我們App發(fā)出的廣播在其它App內(nèi)的廣播接收者收不到并且其它App發(fā)出的廣播我們App內(nèi)的廣播接收者不接收。App內(nèi)廣播的實(shí)現(xiàn)有兩種方式:
    方式一:
    在注冊(cè)廣播時(shí)android:exported="false"這樣就接收不到其它App發(fā)出的廣播了,如下:
<receiver
    //廣播接收者的類名路徑
    android:name=".MyReceiver"
    //發(fā)送廣播的app應(yīng)該具有的權(quán)限
    android:permission="com.jrmf360.permission.send"
    //廣播是否可用,為true時(shí)系統(tǒng)才會(huì)初始化該廣播
    android:enabled="true"
    //是否能接受其他app發(fā)出的廣播,當(dāng)有intent-filter時(shí)默認(rèn)為true
    android:exported="false">
    <intent-filter>
        //接受到的廣播類型,可以使用系統(tǒng)已有的廣播類型也可以自定義
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

單單接收不到其它App發(fā)出的廣播還不能滿足我們的要求,還需要我們自己發(fā)出的廣播其它App接收不到,這就需要在發(fā)送廣播的時(shí)候設(shè)置包名:

Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//設(shè)置包名
intent.setPackage("應(yīng)用包名");
sendBroadcast(intent);

方式二:
使用LocalBroadcastManager也可以實(shí)現(xiàn)應(yīng)用內(nèi)廣播,需要注意的是只有動(dòng)態(tài)注冊(cè)的廣播才能使用LocalBroadcastManager并且廣播的注冊(cè),發(fā)送和反注冊(cè)都要使用LocalBroadcastManager.getInstance(this)的單例對(duì)象去完成。示例如下:

@Override
protected void onResume() {
    super.onResume();
    broadcastReceiver = new MyBroadcastReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.jrmf360.sendrp");
    LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,intentFilter);
}


@Override
protected void onPause() {
    super.onPause();
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}


@Override
public void onClick(View v) {
    int id = v.getId();
    if (id == R.id.btn_sendBroadcast){
        Intent intent = new Intent();
        intent.setAction("com.jrmf360.sendrp");
        Bundle bundle = new Bundle();
        bundle.putString("id","123456");
        bundle.putString("name","zhangsan");
        intent.putExtras(bundle);
        sendBroadcast(intent);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}
  1. 廣播的權(quán)限
    廣播的權(quán)限分為廣播發(fā)送者要求廣播接收者具有的權(quán)限和廣播接收者要求廣播發(fā)送者具有的權(quán)限。
  • 廣播接收者要求廣播發(fā)送者具有的權(quán)限
    在我們?cè)趚ml文件中注冊(cè)廣播接收者的時(shí)候有這樣一行屬性android:permission="String" 我們可以在此添加對(duì)應(yīng)的權(quán)限,這就要求發(fā)出廣播的App必須擁有此權(quán)限,下面演示一下流程:
    先在xml中聲明廣播接收者并添加權(quán)限
<receiver
    android:name=".MyReceiver2"
    //添加自定義權(quán)限
    android:permission="com.jrmf360.permission.send2"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

然后我們需要在xml文件中聲明該權(quán)限

<!--權(quán)限聲明-->
<permission android:name="com.jrmf360.permission.send2"/>

最后廣播發(fā)送者所在的App需要在xml中注冊(cè)該權(quán)限

<uses-permission android:name="com.jrmf360.permission.send2"/>
  • 廣播發(fā)送者要求廣播接收者具有的權(quán)限
    首先調(diào)用發(fā)送廣播方法時(shí)我們需要傳入對(duì)應(yīng)的權(quán)限
sendBroadcast(intent,"com.jrmf360.rp");

接著在xml文件中聲明該權(quán)限

<permission android:name="com.jrmf360.rp"/>

最后廣播接收者所在的App需要在xml中注冊(cè)該權(quán)限

<!--權(quán)限注冊(cè)-->
<uses-permission android:name="com.jrmf360.rp"/>

廣播的權(quán)限是對(duì)廣播接收者隨意接收廣播的限制和保護(hù)。

總結(jié):廣播可以跨線程、進(jìn)程和App通信,我們?cè)陂_發(fā)過(guò)程中可以很方便的利用它來(lái)傳遞消息。但我們也不能濫用廣播可能會(huì)導(dǎo)致接收消息的地方過(guò)多不好維護(hù)而且廣播接收者相對(duì)是個(gè)比較重的組件,比如線程間通信我們可以通過(guò)Handler解決,沒(méi)有必要使用廣播。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容