Android 之 BroadcastReceiver

概念

作為Android的四大主件之一,這種組件本質(zhì)上是一種全局的監(jiān)聽器,用于監(jiān)聽系統(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)廣播,我們便可以通過繼承BroadcastReceiver來創(chuàng)建自己的廣播接收器監(jiān)聽這些標(biāo)準(zhǔn)廣播,但廣播到達(dá)時(shí)執(zhí)行自己的操作。

使用場景

Android廣播分為兩個(gè)方面:廣播發(fā)送者和廣播接收者,通常情況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)。廣播作為Android組件間的通信方式,可以使用的場景如下:
1.同一app內(nèi)部的同一組件內(nèi)的消息通信(單個(gè)或多個(gè)線程之間);----無意義 可以采用Handler 沒必要
2.同一app內(nèi)部的不同組件之間的消息通信(單個(gè)進(jìn)程);————應(yī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)的角度來看,Android中的廣播將廣播的發(fā)送者和接受者極大程度上解耦,使得系統(tǒng)能夠方便集成,更易擴(kuò)展。

一、廣播創(chuàng)建

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

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

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

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

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

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

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

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

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

常駐型:
當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來,程序也會(huì)被系統(tǒng)調(diào)用自動(dòng)運(yùn)行(程序退出,進(jìn)程未被殺死的情況下)。
通過幾次測試,現(xiàn)在的一鍵殺死,殺死應(yīng)用程序進(jìn)程后無法被掉起,接著由于系統(tǒng)權(quán)限越來越??B,現(xiàn)在當(dāng)監(jiān)聽到系統(tǒng)開機(jī)廣播時(shí)也無法調(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)短信到達(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ā)送,分為兩種類型:無序有序

//無序:直接傳遞一個(gè)意圖
sendBroadcast(Intent intent)
//無序:傳遞一個(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:初始碼  通過getResultCode()可以獲取
//initialData:初始數(shù)據(jù)  通過getResultData()可以獲取
//initialExtras:初始附加功能  通過getResultExtras(true)可以獲取
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) 

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

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

無序,有權(quán)限

  Intent intent = new Intent();
  intent.setAction(MyBroadCastReceiver.LOAD_MSG);
  //發(fā)送無序帶權(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) {
        //自定義廣播 通過代碼注冊(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塊錢");
                }
                //重新設(shè)置result值,那么下一個(gè)廣播接受到的值就是我們修改過后的值.
                setResult(40,"我修改了",resultExtras);

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

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

<!--自定義權(quán)限:
normal:低風(fēng)險(xiǎn)權(quán)限,只要申請(qǐng)了就可以使用,安裝時(shí)不需要用戶確認(rèn) 
dangerous:高風(fēng)險(xiǎn)權(quán)限,安裝時(shí)需要用戶的確認(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");

第二種場景: 誰有權(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> 

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

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

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

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

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

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

本地廣播是無法通過靜態(tài)注冊(cè)的方式來接收的。因?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();
        //意圖過濾器
        IntentFilter filter = new IntentFilter();
        //添加行為
        filter.addAction(LocalReceiver.FLAG);
        instance.registerReceiver(localReceiver,filter);
    }

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

LocalReceiver 與我們之前的廣播創(chuàng)建沒區(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)大,不過在實(shí)際中用得還是比較少,應(yīng)用內(nèi)的通信我個(gè)人更喜歡用EventBus。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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