BroadcastReceiver總結(jié)和封裝

一. 介紹

廣播,是一個(gè)全局的監(jiān)聽(tīng)器,屬于Android四大組件之一. 主要用于監(jiān)聽(tīng) / 接收 應(yīng)用 App 發(fā)出的廣播消息,并 做出響應(yīng).

應(yīng)用場(chǎng)景有:

  • Android不同組件間的通信(含 :應(yīng)用內(nèi) / 不同應(yīng)用之間)
  • 多線程通信
  • Android 系統(tǒng)在特定情況下的通信

二. 分類

廣播被分為兩種不同的類型:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。普通廣播是完全異步的,可以在同一時(shí)刻(邏輯上)被所有接收者接收到,消息傳遞的效率比較高,但缺點(diǎn)是:接收者不能將處理結(jié)果傳遞給下一個(gè)接收者,并且無(wú)法終止廣播Intent的傳播;然而有序廣播是按照接收者聲明的優(yōu)先級(jí)別(聲明在intent-filter元素的android:priority屬性中,數(shù)越大優(yōu)先級(jí)別越高,取值范圍:-1000到1000。也可以調(diào)用IntentFilter對(duì)象的setPriority()進(jìn)行設(shè)置),被接收者依次接收廣播。如:A的級(jí)別高于B,B的級(jí)別高于C,那么,廣播先傳給A,再傳給B,最后傳給C。A得到廣播后,可以往廣播里存入數(shù)據(jù),當(dāng)廣播傳給B時(shí),B可以從廣播中得到A存入的數(shù)據(jù)。

靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)的區(qū)別:


image
1. 靜態(tài)注冊(cè)(8.0以后無(wú)法使用)
1.無(wú)序廣播,開(kāi)發(fā)者自身定義 intent的廣播(最常用)
  1. 發(fā)送方式,顯示隱式都可以

      public void normalListener(View view) {
    //        Intent intent = new Intent(this, NormalBroadCaseReceiver.class);
    //        intent.putExtra("key","broad");
    //        sendBroadcast(intent);
            //隱式啟動(dòng),如果有多個(gè)靜態(tài)注冊(cè)的廣播action 相同,都會(huì)收到
    //        Intent intent1 = new Intent();
    //        intent1.setAction("com.kiwilss.broadcase1");
    //        intent1.putExtra("key", "broad");
    //        sendBroadcast(intent1);
    
            //8.0以后,想發(fā)送成功就要加上 intent1.setPackage(getPackageName());
            //原因:谷歌在8.0后為了提高效率,刪除了靜態(tài)注冊(cè),防止關(guān)閉App后廣播還在,
            // 造成內(nèi)存泄漏, 現(xiàn)在靜態(tài)注冊(cè)的廣播需要指定包名,而動(dòng)態(tài)注冊(cè)就沒(méi)有這個(gè)問(wèn)題
            Intent intent1 = new Intent();
            intent1.setAction("com.kiwilss.broadcase1");
            intent1.putExtra("key", "broad");
            intent1.setPackage(getPackageName());
            sendBroadcast(intent1);
        }
    
  2. 自定義廣播

    NormalBroadCaseReceiver

  3. public class NormalBroadCaseReceiver extends BroadcastReceiver {
        @SuppressLint("UnsafeProtectedBroadcastReceiver")
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "onReceive: "+intent.getStringExtra("key") );
        }
    }
    

    NormalBroadCaseReceiver2

    public class NormalBroadCaseReceiver2 extends BroadcastReceiver {
        @SuppressLint("UnsafeProtectedBroadcastReceiver")
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "normal2-------onReceive: "+intent.getStringExtra("key") );
        }
    }
    
  4. AndroidManifest.xml里注冊(cè)

    • 屬性說(shuō)明:
    <receiver 
        android:enabled=["true" | "false"]
    //此broadcastReceiver能否接收其他App的發(fā)出的廣播
    //默認(rèn)值是由receiver中有無(wú)intent-filter決定的:如果有intent-filter,默認(rèn)值為true,否則為false
        android:exported=["true" | "false"]
        android:icon="drawable resource"
        android:label="string resource"
    //繼承BroadcastReceiver子類的類名
        android:name=".mBroadcastReceiver"
    //具有相應(yīng)權(quán)限的廣播發(fā)送者發(fā)送的廣播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver運(yùn)行所處的進(jìn)程
    //默認(rèn)為app的進(jìn)程,可以指定獨(dú)立的進(jìn)程
    //注:Android四大基本組件都可以通過(guò)此屬性指定自己的獨(dú)立進(jìn)程
        android:process="string" >
    
    //用于指定此廣播接收器將接收的廣播類型
    //本示例中給出的是用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播
     <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    
    • 注冊(cè)示例
      <receiver android:name=".broadcastreceiver.NormalBroadCaseReceiver" tools:ignore="ExportedReceiver">
                <intent-filter>
                    <action android:name="com.kiwilss.broadcase1"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </receiver>
    
            <receiver android:name=".broadcastreceiver.NormalBroadCaseReceiver2" tools:ignore="ExportedReceiver">
                <intent-filter>
                    <action android:name="com.kiwilss.broadcase1"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </receiver>
    

    當(dāng)此 App首次啟動(dòng)時(shí),系統(tǒng)會(huì)自動(dòng)實(shí)例化mBroadcastReceiver類,并注冊(cè)到系統(tǒng)中。

  5. 測(cè)試結(jié)果

    04-18 15:52:46.360 12305-12305/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: broad
    04-18 15:52:46.363 12305-12305/com.kiwilss.lxkj.fourassembly E/MMM: normal2-------onReceive: broad

2.有序廣播,靜態(tài)注冊(cè)
  1. 發(fā)送方法和無(wú)序廣播類似

    public void orderlyListener(View view) {
        //和無(wú)序廣播類似
        Intent intent1 = new Intent();
        intent1.setAction("com.kiwilss.broadcase2");
        //intent1.putExtra("key","broad");
        intent1.putExtra("key", "orderly");
        //null 表示沒(méi)有權(quán)限限制
        sendOrderedBroadcast(intent1, null);
    }
    
  2. 自定義廣播, 有序廣播可以中斷傳遞, 也可以新增傳遞信息

    OrderlyBroadcastReceiver

    public class OrderlyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "onReceive: "+intent.getStringExtra("key"));
    
            if (TextUtils.equals("orderly",intent.getStringExtra("key"))) {
                //中斷傳遞
                abortBroadcast();
            }else {
                //傳遞新的信息給下一個(gè)廣播
                Bundle bundle = new Bundle();
                bundle.putString("broad","新的信息");
                setResultExtras(bundle);
            }
        }
    }
    
    

    OrderlyBroadcastReceiver2

    public class OrderlyBroadcastReceiver2 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e(TAG, "onReceive22 : "+intent.getStringExtra("key"));
    
            Bundle bundle = getResultExtras(true);
            String broad = bundle.getString("broad");
            Log.e(TAG, "onReceive222 : "+ broad );
        }
    }
    
  3. 在清單文件中設(shè)定優(yōu)先級(jí)

    <!--可以設(shè)置廣播的優(yōu)先級(jí)-->
    <receiver android:name=".broadcastreceiver.OrderlyBroadcastReceiver"
    >
        <intent-filter android:priority="100">
            <action android:name="com.kiwilss.broadcase2"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </receiver>
    <receiver android:name=".broadcastreceiver.OrderlyBroadcastReceiver2"
    >
        <intent-filter android:priority="90">
            <action android:name="com.kiwilss.broadcase2"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </receiver>
    

4.測(cè)試結(jié)果

中斷傳播結(jié)果:

04-18 16:23:30.380 15795-15795/com.kiwilss.lxkj.fourassembly E/MMM: onReceive:  orderly

新增信息結(jié)果:

04-18 16:30:35.026 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: broad
04-18 16:30:35.074 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive22 : broad
04-18 16:30:35.074 16342-16342/com.kiwilss.lxkj.fourassembly E/MMM: onReceive222 : 新的信息

3.動(dòng)態(tài)注冊(cè)
  1. 發(fā)送方式

    /**普通廣播,動(dòng)態(tài)注冊(cè)
     * @param view
     */
    public void normalDynamicListener(View view) {
        //動(dòng)態(tài)注冊(cè)廣播發(fā)送消息
        Intent intent = new Intent("com.kiwilss.normaldynamic");
        intent.putExtra("key","普通廣播動(dòng)態(tài)注冊(cè)");
        sendBroadcast(intent);
    }
    
  2. 注冊(cè)和解除注冊(cè)

     @Override
        protected void onResume() {
            super.onResume();
            //普通廣播注冊(cè)
            if (mBroadcastReceiver == null){
                mBroadcastReceiver = new LocalBroadcastReceiver();
                IntentFilter intentFilter = new IntentFilter("com.kiwilss.normaldynamic");
                registerReceiver(mBroadcastReceiver,intentFilter);
            }
        }
    
    @Override
    protected void onPause() {
        super.onPause();
        //普通廣播解除注冊(cè)
        if (mBroadcastReceiver != null) {
            unregisterReceiver(mBroadcastReceiver);
        }
    }
    
  • 動(dòng)態(tài)廣播最好在ActivityonResume()注冊(cè)、onPause()注銷。
  • 原因:
    1. 對(duì)于動(dòng)態(tài)廣播,有注冊(cè)就必然得有注銷,否則會(huì)導(dǎo)致內(nèi)存泄露
    2. 重復(fù)注冊(cè)、重復(fù)注銷也不允許
  1. 自定義廣播

  2. class LocalBroadcastReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("MMM", "onReceive: " + intent.getStringExtra("key"));
        }
    }
    
  3. 測(cè)試結(jié)果

    04-18 16:52:14.730 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 普通廣播動(dòng)態(tài)注冊(cè)

4. 本地廣播 App應(yīng)用內(nèi)廣播(Local Broadcast)
  1. Android中的廣播可以跨App直接通信(exported對(duì)于有intent-filter情況下默認(rèn)值為true)

可能出現(xiàn)的問(wèn)題:

  • 其他App針對(duì)性發(fā)出與當(dāng)前App intent-filter相匹配的廣播,由此導(dǎo)致當(dāng)前App不斷接收廣播并處理;

  • 其他App注冊(cè)與當(dāng)前App一致的intent-filter用于接收廣播,獲取廣播具體信息;
    即會(huì)出現(xiàn)安全性 & 效率性的問(wèn)題。

  • 解決方案
    使用App應(yīng)用內(nèi)廣播(Local Broadcast)

    1. App應(yīng)用內(nèi)廣播可理解為一種局部廣播,廣播的發(fā)送者和接收者都同屬于一個(gè)App。

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

  1. 具體使用

    發(fā)送方法:

     /**
         * 對(duì)于LocalBroadcastManager方式發(fā)送的應(yīng)用內(nèi)廣播,
         * 只能通過(guò)LocalBroadcastManager動(dòng)態(tài)注冊(cè),不能靜態(tài)注冊(cè)
         *
         * @param view
         */
        public void appListener(View view) {
            //注冊(cè)應(yīng)用內(nèi)廣播接收器
            //步驟1:實(shí)例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver
            mBroadcastReceiver = new LocalBroadcastReceiver();
            IntentFilter intentFilter = new IntentFilter();
            //步驟2:實(shí)例化LocalBroadcastManager的實(shí)例
            localBroadcastManager = LocalBroadcastManager.getInstance(this);
    //步驟3:設(shè)置接收廣播的類型
            intentFilter.addAction("com.kiwilss.local");
    //4,注冊(cè)
            localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
    
    
            //發(fā)送應(yīng)用內(nèi)廣播測(cè)試
            Intent intent = new Intent("com.kiwilss.local");
            intent.putExtra("key","本地廣播");
            localBroadcastManager.sendBroadcast(intent);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //取消注冊(cè)本地廣播
            if (mBroadcastReceiver != null)
            localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
    
            //取消網(wǎng)絡(luò)監(jiān)聽(tīng)注冊(cè)
            if (netBroadcastReceiver != null)
                unregisterReceiver(netBroadcastReceiver);
        }
    
  2. 自定義廣播

  3. class LocalBroadcastReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.e("MMM", "onReceive: " + intent.getStringExtra("key"));
        }
    }
    
  4. 測(cè)試結(jié)果

    04-18 16:57:28.905 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 本地廣播

5.系統(tǒng)廣播(System Broadcast)
  1. 介紹
  • Android中內(nèi)置了多個(gè)系統(tǒng)廣播:只要涉及到手機(jī)的基本操作(如開(kāi)機(jī)、網(wǎng)絡(luò)狀態(tài)變化、拍照等等),都會(huì)發(fā)出相應(yīng)的廣播

  • 每個(gè)廣播都有特定的Intent - Filter(包括具體的action),Android系統(tǒng)廣播action如下:

  • 系統(tǒng)操作 action
    監(jiān)聽(tīng)網(wǎng)絡(luò)變化 android.net.conn.CONNECTIVITY_CHANGE
    關(guān)閉或打開(kāi)飛行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED
    充電時(shí)或電量發(fā)生變化 Intent.ACTION_BATTERY_CHANGED
    電池電量低 Intent.ACTION_BATTERY_LOW
    電池電量充足(即從電量低變化到飽滿時(shí)會(huì)發(fā)出廣播 Intent.ACTION_BATTERY_OKAY
    系統(tǒng)啟動(dòng)完成后(僅廣播一次) Intent.ACTION_BOOT_COMPLETED
    按下照相時(shí)的拍照按鍵(硬件按鍵)時(shí) Intent.ACTION_CAMERA_BUTTON
    屏幕鎖屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
    設(shè)備當(dāng)前設(shè)置被改變時(shí)(界面語(yǔ)言、設(shè)備方向等) Intent.ACTION_CONFIGURATION_CHANGED
    插入耳機(jī)時(shí) Intent.ACTION_HEADSET_PLUG
    未正確移除SD卡但已取出來(lái)時(shí)(正確移除方法:設(shè)置--SD卡和設(shè)備內(nèi)存--卸載SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL
    插入外部?jī)?chǔ)存裝置(如SD卡) Intent.ACTION_MEDIA_CHECKING
    成功安裝APK Intent.ACTION_PACKAGE_ADDED
    成功刪除APK Intent.ACTION_PACKAGE_REMOVED
    重啟設(shè)備 Intent.ACTION_REBOOT
    屏幕被關(guān)閉 Intent.ACTION_SCREEN_OFF
    屏幕被打開(kāi) Intent.ACTION_SCREEN_ON
    關(guān)閉系統(tǒng)時(shí) Intent.ACTION_SHUTDOWN
    重啟設(shè)備 Intent.ACTION_REBOOT

    注:當(dāng)使用系統(tǒng)廣播時(shí),只需要在注冊(cè)廣播接收者時(shí)定義相關(guān)的action即可,并不需要手動(dòng)發(fā)送廣播,當(dāng)系統(tǒng)有相關(guān)操作時(shí)會(huì)自動(dòng)進(jìn)行系統(tǒng)廣播

  1. 使用示例, 監(jiān)聽(tīng)手機(jī)網(wǎng)絡(luò)狀態(tài)

    1. 方法

      /**系統(tǒng)廣播,示例監(jiān)聽(tīng)網(wǎng)絡(luò)
       * @param view
       */
      NetBroadcastReceiver netBroadcastReceiver;
      public void appDynamicListener(View view) {
           netBroadcastReceiver = new NetBroadcastReceiver();
          IntentFilter intentFilter = new IntentFilter();
          intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
          registerReceiver(netBroadcastReceiver,intentFilter);
          //ondestory中取消注冊(cè)
      }
      
    2. 自定義廣播

      class NetBroadcastReceiver extends BroadcastReceiver{
      
          @Override
          public void onReceive(Context context, Intent intent) {
              Log.e("MMM", "onReceive: 網(wǎng)絡(luò)有變化" );
              //連接或是關(guān)閉網(wǎng)絡(luò)時(shí)可以監(jiān)控
          }
      }
      
    3. 測(cè)試, 關(guān)閉打開(kāi)手機(jī)網(wǎng)絡(luò)

      04-18 17:02:03.220 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化
      04-18 17:02:06.834 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化
      04-18 17:02:13.506 17989-17989/com.kiwilss.lxkj.fourassembly E/MMM: onReceive: 網(wǎng)絡(luò)有變化

三.動(dòng)態(tài)注冊(cè)幫助類

鑒于各種限制,盡量使用動(dòng)態(tài)注冊(cè),所以寫(xiě)了一個(gè)幫助類,方便快速注冊(cè)和解除注冊(cè)廣播。比較簡(jiǎn)單,代碼如下:

object BroadcastKtx {
    /**
     * 注冊(cè)廣播
     *
     * @param context
     * @param broadcastReceiver
     * @param action
     */
    fun registerBroadcast(
        context: Context?,
        broadcastReceiver: BroadcastReceiver?,
        vararg action: String?
    ) {
        if (context == null || broadcastReceiver == null) return
        val intentFilter = IntentFilter()
        for (element in action) {
            intentFilter.addAction(element)
        }
        context.registerReceiver(broadcastReceiver, intentFilter)
    }

    /**
     * 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
     *
     * @param context
     * @param broadcastReceiver
     */
    fun unregisterBroadcast(context: Context?, broadcastReceiver: BroadcastReceiver?) {
        if (context == null || broadcastReceiver == null) return
        context.unregisterReceiver(broadcastReceiver)
    }
}

/**
 * 注冊(cè)廣播
 *
 * @param broadcastReceiver
 * @param action
 */
fun Context?.registerBroadcast(
    broadcastReceiver: BroadcastReceiver?,
    vararg action: String?
) = BroadcastKtx.registerBroadcast(this, broadcastReceiver, *action)

/**
 * 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
 *
 * @param broadcastReceiver
 */
fun Context?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
    BroadcastKtx.unregisterBroadcast(this, broadcastReceiver)


/**
 * 注冊(cè)廣播
 *
 * @param broadcastReceiver
 * @param action
 */
fun Fragment?.registerBroadcast(
    broadcastReceiver: BroadcastReceiver?,
    vararg action: String?
) = BroadcastKtx.registerBroadcast(this?.context, broadcastReceiver, *action)

/**
 * 解除注冊(cè)廣播,廣播要和注冊(cè)時(shí)是同一個(gè)
 *
 * @param broadcastReceiver
 */
fun Fragment?.unregisterBroadcast(broadcastReceiver: BroadcastReceiver?) =
    BroadcastKtx.unregisterBroadcast(this?.context, broadcastReceiver)

使用很簡(jiǎn)單,在 Activity/Fragment 中直接使用,示例 demo如下:

class BroadcastActivity: AppCompatActivity(R.layout.activity_broadcast) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


       btnSend.setOnClickListener {
            //發(fā)送廣播信息
           sendBroadcast(createIntentBroadcast(action1,"broad" to "test broadcast"))
       }


    }
    //初始化廣播
    val mTestBroadcast by lazy { TestBroadcast() }
    val action1 = "com.kiwilss.broadcase1"
    override fun onResume() {
        super.onResume()
        //注冊(cè),可以注冊(cè)很多個(gè) action
        registerBroadcast(mTestBroadcast,action1)
    }

    override fun onPause() {
        super.onPause()
        unregisterBroadcast(mTestBroadcast)
    }
}

class TestBroadcast: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val action = intent?.action
        Log.e("MMM", "onReceive: $action --- ${intent?.getStringExtra("broad")}");
    }
}

四.參考和地址

Android四大組件:BroadcastReceiver史上最全面解析

Android 四大組件

demo

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

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

  • 一、簡(jiǎn)介 廣播是任何應(yīng)用均可接收的消息,分為標(biāo)準(zhǔn)廣播和有序廣播兩種。系統(tǒng)將針對(duì)系統(tǒng)事件(例如:系統(tǒng)啟動(dòng)或設(shè)備開(kāi)始充...
    Diffey閱讀 819評(píng)論 0 2
  • 現(xiàn)實(shí)中的廣播:電臺(tái)為了傳達(dá)一些消息而發(fā)送廣播,通過(guò)廣播攜帶要傳達(dá)的消息,群眾只要買一個(gè)收音機(jī),就可以收到廣播了。 ...
    stevewang閱讀 4,375評(píng)論 0 8
  • 定義 廣播接收器是一個(gè)全局的監(jiān)聽(tīng)器,屬于四大組件之一。Android廣播分為兩個(gè)角色:廣播發(fā)送者、廣播接收者。 應(yīng)...
    呂注意閱讀 1,520評(píng)論 0 1
  • 零、資料 前輩文章I 前輩文章II 前輩文章III 一、簡(jiǎn)介 Android 四大組件中的全局的監(jiān)聽(tīng)器,Broad...
    Demon鑫閱讀 527評(píng)論 0 1
  • 一. BroadcastReceiver簡(jiǎn)介 1.1 BroadcastReceiver定義(What?) And...
    Marker_Sky閱讀 3,563評(píng)論 0 1

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