Android 之 BroadcastReceiver

概念

作為Android的四大主件之一,這種組件本質(zhì)上是一種全局的監(jiān)聽(tīng)器,用于監(jiān)聽(tīng)系統(tǒng)全局的廣播消息,以及在應(yīng)用程序之間進(jìn)行信息的傳輸。
例如:
當(dāng)電池電量低、系統(tǒng)的時(shí)間變化、系統(tǒng)收到短信等等,這些狀態(tài)發(fā)生時(shí)系統(tǒng)會(huì)對(duì)外發(fā)送標(biāo)準(zhǔn)廣播,我們便可以通過(guò)繼承BroadcastReceiver來(lái)創(chuàng)建自己的廣播接收器監(jiān)聽(tīng)這些標(biāo)準(zhǔn)廣播,但廣播到達(dá)時(shí)執(zhí)行自己的操作。

使用場(chǎng)景

Android廣播分為兩個(gè)方面:廣播發(fā)送者和廣播接收者,通常情況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)。廣播作為Android組件間的通信方式,可以使用的場(chǎng)景如下:
1.同一app內(nèi)部的同一組件內(nèi)的消息通信(單個(gè)或多個(gè)線程之間);----無(wú)意義 可以采用Handler 沒(méi)必要
2.同一app內(nèi)部的不同組件之間的消息通信(單個(gè)進(jìn)程);————應(yīng)用場(chǎng)景較少。
3.同一app具有多個(gè)進(jìn)程的不同組件之間的消息通信;
4.不同app之間的組件之間消息通信;
5.Android系統(tǒng)在特定情況下與App之間的消息通信。

其中3、4、5 需要對(duì)不同進(jìn)程間的消息通信,此時(shí)根據(jù)實(shí)際業(yè)務(wù)使用廣播機(jī)制會(huì)顯得非常適宜。

Android中的廣播使用了觀察者模式,基于消息的發(fā)布/訂閱事件模型。因此,從實(shí)現(xiàn)的角度來(lái)看,Android中的廣播將廣播的發(fā)送者和接受者極大程度上解耦,使得系統(tǒng)能夠方便集成,更易擴(kuò)展。

一、廣播創(chuàng)建

廣播創(chuàng)建不要太簡(jiǎn)單,作為四大主件之一,都有一共同的特點(diǎn)繼承
此處我們寫(xiě)一個(gè)類(lèi)繼承BroadcastReceiver,并重寫(xiě)其抽象方法onReceive()

//自定義廣播 
public class MyBroadCastReceiver extends BroadcastReceiver {

    //注冊(cè)的廣播行為,此處行為可以是任意字符串,只需要匹配我們廣播注冊(cè)時(shí)的行為就可以,行為可以是多個(gè)。
    //這里我寫(xiě)的是系統(tǒng)短信到達(dá)的行為
    public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        //通過(guò)代碼 匹配 廣播發(fā)送時(shí)的行為,當(dāng)行為匹配,執(zhí)行我們的操作
        if(intent.getAction().equals(INENT_ACTION)){
            Log.e(TAG,"匹配到我們的意圖");
        }

    }
}
二、廣播注冊(cè)

想要監(jiān)聽(tīng)廣播,必然需要注冊(cè)屬于我們的廣播接收器
這里我們有兩種注冊(cè)方式:

1種: 代碼中動(dòng)態(tài)注冊(cè)
2種: 在Manifest.xml中靜態(tài)注冊(cè)

注冊(cè)方式不同,當(dāng)然是有所區(qū)別的

非常駐型:
當(dāng)你使用第一種方式注冊(cè)的時(shí)候,該廣播接收器不是常駐類(lèi)型,也就是說(shuō)廣播的接收器會(huì)跟隨主件,程序的生命周期銷(xiāo)毀而銷(xiāo)毀,一般我們會(huì)自己手動(dòng)進(jìn)行解除注冊(cè),避免內(nèi)存的泄漏。

常駐型:
當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來(lái),程序也會(huì)被系統(tǒng)調(diào)用自動(dòng)運(yùn)行(程序退出,進(jìn)程未被殺死的情況下)。
通過(guò)幾次測(cè)試,現(xiàn)在的一鍵殺死,殺死應(yīng)用程序進(jìn)程后無(wú)法被掉起,接著由于系統(tǒng)權(quán)限越來(lái)越??B,現(xiàn)在當(dāng)監(jiān)聽(tīng)到系統(tǒng)開(kāi)機(jī)廣播時(shí)也無(wú)法調(diào)起我們的App廣播。除非你的應(yīng)用程序被注冊(cè)到系統(tǒng)應(yīng)用程序內(nèi)。

注冊(cè)方式一:清單注冊(cè)

  <receiver android:name=".MyBroadCastReceiver">
      <!-- android:priority屬性是設(shè)置此接收者的優(yōu)先級(jí)(從-1000到1000)
        用于有序廣播,值越大 , 優(yōu)先級(jí)越高
        同級(jí)別接收是先后隨機(jī)的;級(jí)別低的收到廣播。
      -->
      <intent-filter android:priority="1000">
              <!--注冊(cè)的廣播行為,此處是監(jiān)聽(tīng)的系統(tǒng)短信到達(dá)-->
             <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
 </receiver>

注冊(cè)方式二:代碼注冊(cè)

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    MyBroadCastReceiver mRecevier;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * 代碼注冊(cè)廣播,隨程序的生命周期
         */
        mRecevier = new MyBroadCastReceiver();
        IntentFilter filter = new IntentFilter();
        //用于攔截短信的action
        filter.setPriority(1000);
        //行為定義,可以是多個(gè)
        filter.addAction("android.provider.Telephony.SMS_RECEIVED");
        //代碼注冊(cè)廣播
        registerReceiver(mRecevier,filter);
    }

   @Override
    protected void onDestroy() {
        //解除廣播注冊(cè)
        unregisterReceiver(mRecevier);
        super.onDestroy();
    }
}
三、廣播發(fā)送

廣播的發(fā)送,分為兩種類(lèi)型:無(wú)序,有序

//無(wú)序:直接傳遞一個(gè)意圖
sendBroadcast(Intent intent)
//無(wú)序:傳遞一個(gè)意圖和權(quán)限
sendBroadcast(Intent intent, String receiverPermission)
//有序:傳遞一個(gè)意圖和權(quán)限
sendOrderedBroadcast(Intent intent,String receiverPermission) 
//有序:傳遞一個(gè)意圖和權(quán)限附加額外信息
//resultReceiver:傳入一個(gè)receiver作為此次有序廣播最后一個(gè)執(zhí)行的廣播
//scheduler:指定要運(yùn)行的廣播接收器的線程。
//initialCode:初始碼  通過(guò)getResultCode()可以獲取
//initialData:初始數(shù)據(jù)  通過(guò)getResultData()可以獲取
//initialExtras:初始附加功能  通過(guò)getResultExtras(true)可以獲取
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) 

代碼演示:
無(wú)序,無(wú)權(quán)限

  Intent intent = new Intent();
  intent.setAction(MyBroadCastReceiver.LOAD_MSG);
  //發(fā)送無(wú)序廣播
  sendBroadcast(intent);

無(wú)序,有權(quán)限

  Intent intent = new Intent();
  intent.setAction(MyBroadCastReceiver.LOAD_MSG);
  //發(fā)送無(wú)序帶權(quán)限廣播
  sendBroadcast(intent,"android.permission.RECEIVE_SMS");

有序,不帶額外參數(shù)

 Intent intent = new Intent();
 intent.setAction(MyBroadCastReceiver.LOAD_TEL);
//有序廣播:根據(jù)廣播優(yōu)先級(jí) 
//第二個(gè)參數(shù) 為 接收的廣播 的 App所具備的權(quán)限 如果為NULL 則不需要權(quán)限
//如果傳遞了權(quán)限,只有在AndroidManifest.xml中要添加對(duì)應(yīng)的權(quán)限的應(yīng)用才能獲取到這條廣播。
sendOrderedBroadcast(intent,null);
sendOrderedBroadcast(intent,"android.permission.RECEIVE_SMS");

有序,帶額外參數(shù)

Intent intent = new Intent();
intent.setAction(MyBroadCastReceiver.LOAD_TEL);

Bundle extras = new Bundle();
extras.putString("extras","其它數(shù)據(jù)");
//此處權(quán)限為null,線程為默認(rèn)主線程
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
   @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG,"最后一個(gè)被調(diào)用的receiver");
        int resultCode = getResultCode();   
        String resultData = getResultData();
        Bundle resultExtras = getResultExtras(true);
    }
 }, null, 10086, "初始數(shù)據(jù)", extras);

關(guān)于有序廣播:
既然廣播是一個(gè)有序的,那么我們就可以在中間攔截低級(jí)別的廣播,并中止廣播的傳遞。利用isOrderedBroadcast()判斷是否是有序廣播
利用abortBroadcast()攔截廣播

public class MyBroadCastReceiver2 extends BroadcastReceiver {

    public static final String INENT_ACTION="android.provider.Telephony.SMS_RECEIVED";

    @Override
    public void onReceive(Context context, Intent intent) {
        //自定義廣播 通過(guò)代碼注冊(cè)
        if(intent.getAction().equals(INENT_ACTION)){
            //判斷是否是  有序廣播
            boolean orderedBroadcast = isOrderedBroadcast();
            if (orderedBroadcast){
                //獲取resultData
                String resultData = getResultData();
                //獲取resultCode
                int resultCode = getResultCode();
                //獲取resultExtras
                Bundle resultExtras = getResultExtras(true);
                if(resultExtras!=null){
                    //修改數(shù)據(jù)
                    resultExtras.putString("extras","你掉了10塊錢(qián)");
                }
                //重新設(shè)置result值,那么下一個(gè)廣播接受到的值就是我們修改過(guò)后的值.
                setResult(40,"我修改了",resultExtras);

                //如果不需要傳遞,可以使用abortBroadcast進(jìn)行攔截
                abortBroadcast();
            }
        }
    }
}
四、權(quán)限問(wèn)題

第一種場(chǎng)景: 誰(shuí)有權(quán)收我的廣播?
首先我們可以聲明一個(gè)自定義權(quán)限
在Androidmanifest.xml中定義

<!--自定義權(quán)限:
normal:低風(fēng)險(xiǎn)權(quán)限,只要申請(qǐng)了就可以使用,安裝時(shí)不需要用戶(hù)確認(rèn) 
dangerous:高風(fēng)險(xiǎn)權(quán)限,安裝時(shí)需要用戶(hù)的確認(rèn)才可使用;
signature:只有當(dāng)申請(qǐng)權(quán)限的應(yīng)用程序的數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序的數(shù)字簽名相同時(shí) (如果是申請(qǐng)系統(tǒng)權(quán)限,則需要與系統(tǒng)簽名相同),才能將權(quán)限授給它;
signatureOrSystem:簽名相同,或者申請(qǐng)權(quán)限的應(yīng)用為系統(tǒng)應(yīng)用(在system image中)。
一般就normal了
-->
<permission 
    android:name = "com.android.permission.my_permission"
    android:protectionLevel="normal"  />  
<!-- 注冊(cè)自定義的權(quán)限 -->
<uses-permission android:name="com.android.permission.my_permission" />

做完以上操作,發(fā)送廣播時(shí)如果帶有此權(quán)限,那么只有注冊(cè)了此權(quán)限的應(yīng)用才能接收這條廣播。sendBroadcast(intent,"com.android.permission.my_permission");

第二種場(chǎng)景: 誰(shuí)有權(quán)給我發(fā)廣播?
當(dāng)我們?cè)谇鍐挝募凶?cè)廣播時(shí)可以加入權(quán)限,此處廣播上添加了權(quán)限。
android:permission="com.android.permission.my_permission"

<receiver android:name=".MyReceiver" 
          android:permission="com.android.permission.my_permission">   
    <intent-filter>  
         <action android:name="com.android.MY_ACTION" />   
    </intent-filter>  
</receiver> 

這樣一來(lái),我們定義的照顧Receiver只能接收來(lái)自具有我們注冊(cè)的權(quán)限的應(yīng)用發(fā)出的廣播了。需要進(jìn)行以下注冊(cè)

<!-- 注冊(cè)自定義的權(quán)限 -->
<uses-permission android:name="com.android.permission.my_permission" />
五、本地廣播

我們看出通過(guò)權(quán)限的控制,我們可以讓自己的廣播只被我們自己的應(yīng)用接收,但是太多的權(quán)限設(shè)置和發(fā)布比較繁瑣。
所以Google給我們提供了一枚專(zhuān)注于應(yīng)用內(nèi)的廣播 LocalBroadcastReceiver(此處的App應(yīng)用以App應(yīng)用進(jìn)程為界)

相比于全局廣播,App應(yīng)用內(nèi)廣播優(yōu)勢(shì)體現(xiàn)在:
1.安全性更高;
2.更加高效。

為此,Android v4兼容包中給出了封裝好的LocalBroadcastManager類(lèi),用于統(tǒng)一處理App應(yīng)用內(nèi)的廣播問(wèn)題,
使用方式上與通常的全局廣播幾乎相同,只是注冊(cè)/取消注冊(cè)廣播接收器和發(fā)送廣播時(shí)將主調(diào)context變成了LocalBroadcastManager的單一實(shí)例。

Android本地廣播機(jī)制:使用這個(gè)機(jī)制發(fā)出的廣播只能夠在應(yīng)用程序的內(nèi)部進(jìn)行傳遞.
并且廣播接收器也只能接收來(lái)自本應(yīng)用程序發(fā)出的廣播,這樣所有的安全性問(wèn)題就都不存在了。

本地廣播是無(wú)法通過(guò)靜態(tài)注冊(cè)的方式來(lái)接收的。因?yàn)橐脮r(shí)程序肯定已經(jīng)啟動(dòng)了。

注冊(cè)與解除注冊(cè)

/**
 * LocalBroadcastManager  應(yīng)用內(nèi)廣播 只針對(duì)自身應(yīng)用廣播
 */
public class LocalReceiverActivity extends AppCompatActivity {

    private LocalBroadcastManager instance;
    private LocalReceiver localReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        //使用此方式注冊(cè)的廣播 只有本應(yīng)用程序才可以廣播到
        instance = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        //意圖過(guò)濾器
        IntentFilter filter = new IntentFilter();
        //添加行為
        filter.addAction(LocalReceiver.FLAG);
        instance.registerReceiver(localReceiver,filter);
    }

    @Override
    protected void onDestroy() {
        instance.unregisterReceiver(localReceiver);
        super.onDestroy();
    }
}

LocalReceiver 與我們之前的廣播創(chuàng)建沒(méi)區(qū)別都繼承至BroadcastReceiver。

public class LocalReceiver extends BroadcastReceiver {

    public static final String FLAG = "localReceiver.com";
    private static final String TAG =FLAG;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG,"應(yīng)用內(nèi)廣播");
    }
}

至此廣播就介紹完了,廣播功能雖然挺強(qiáng)大,不過(guò)在實(shí)際中用得還是比較少,應(yīng)用內(nèi)的通信我個(gè)人更喜歡用EventBus。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容