Android 廣播

1. 應用場景

  • 同一個App內部的同一組件內的消息通信(單個或者多個線程之間);
  • 同一個App內部的不同組件之間的消息通信(單個進程);
  • 同一個App具有多個進程的不同組件之間的消息通信;
  • 不同App之間的組件之間消息通信;
  • Android系統在特定的情況下與App之間的消息通信。

2. 實現原理

2.1 模型

觀察者模式:基於消息的發佈 / 訂閱事件模型
好處:Android將廣播的發送者 和 接收者 解耦,使得系統方便集成,更易擴展 廣播模型

3. 用法

使用流程: 使用流程

3.1 自定義廣播接收者

  • 繼承BroadcastReceivre基類
  • 必須複寫抽象方法onReceive()方法

廣播接收器接收到相應廣播後,會自動回調 onReceive() 方法 一般情況下,onReceive方法會涉及 與 其他組件之間的交互,如發送Notification、啓動Service等 默認情況下,廣播接收器運行在 UI 線程,因此,onReceive()方法不能執行耗時操作,否則將導致ANR

// 繼承BroadcastReceivre基類
public class mBroadcastReceiver extends BroadcastReceiver {

  // 複寫onReceive()方法
  // 接收到廣播後,則自動調用該方法
  @Override
  public void onReceive(Context context, Intent intent) {
   //寫入接收廣播後的操作
    }
}

3.2 註冊廣播接收器

註冊的方式分爲兩種:靜態註冊、動態註冊

3.3 靜態註冊

  • 註冊方式:在AndroidManifest.xml裏通過標籤聲明
  • 當此App首次啓動時,系統會自動實例化mBroadcastReceiver類,並註冊到系統中。
<receiver 
    android:enabled=["true" | "false"]
    //此broadcastReceiver能否接收其他App的發出的廣播
    //默認值是由receiver中有無intent-filter決定的:如果有intent-filter,默認值爲true,否則爲false
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    //繼承BroadcastReceiver子類的類名
    android:name=".mBroadcastReceiver"
    //具有相應權限的廣播發送者發送的廣播才能被此BroadcastReceiver所接收;
    android:permission="string"
    //BroadcastReceiver運行所處的進程
    //默認爲app的進程,可以指定獨立的進程
    //注:Android四大基本組件都可以通過此屬性指定自己的獨立進程
    android:process="string" >

//用於指定此廣播接收器將接收的廣播類型
//本示例中給出的是用於接收網絡狀態改變時發出的廣播
 <intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 
//有的時候要給相應權限

注意 :Android8.0的後臺執行限制

  1. 應用無法使用清單註冊(靜態註冊)的方式來接收隱式廣播
  2. 具體廣播限制和對應赦免清單
    避免:不用靜態註冊,或者不用隱式廣播
    參考:
    咦,Oreo怎麼收不到廣播了?
    android 8.0靜態註冊的隱式廣播接收不到
    Android8.0 靜態廣播的改動(可跨進程)

3.4 動態註冊

  • 註冊方式:在代碼中調用Context.registerReceiver()方法
  • 注意廣播銷燬的位置

    兩個APP間收發廣播,放在後臺的app只能在onDestory()裏銷燬才能收到廣播

// 選擇在Activity生命週期方法中的onResume()中註冊
@Override
  protected void onResume(){
      super.onResume();

    // 1. 實例化BroadcastReceiver子類 &  IntentFilter
     mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
     IntentFilter intentFilter = new IntentFilter();

    // 2. 設置接收廣播的類型
    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

    // 3. 動態註冊:調用Context的registerReceiver()方法
     registerReceiver(mBroadcastReceiver, intentFilter);
 }


// 註冊廣播後,要在相應位置記得銷燬廣播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 當此Activity實例化時,會動態將MyBroadcastReceiver註冊到系統中
// 當此Activity銷燬時,動態註冊的MyBroadcastReceiver將不再接收到相應的廣播。
 @Override
 protected void onPause() {
     super.onPause();
      //銷燬在onResume()方法中的廣播
     unregisterReceiver(mBroadcastReceiver);
     }
}

注意

  1. 對於動態廣播,有註冊就必然得有註銷,否則會導致內存泄露
    在onResume()註冊、onPause()註銷是因爲onPause()在App死亡前一定會被執行,從而保證廣播在App死亡前一定會被註銷,從而防止內存泄露。
  1. 不在onCreate() & onDestory() 或 onStart() & onStop()註冊、註銷是因爲: 當系統因爲內存不足(優先級更高的應用需要內存,請看上圖紅框)要回收Activity佔用的資源時,Activity在執行完onPause()方法後就會被銷燬,有些生命週期方法onStop(),onDestory()就不會執行。當再回到此Activity時,是從onCreate方法開始執行。
  2. 假設我們將廣播的註銷放在onStop(),onDestory()方法裏的話,有可能在Activity被銷燬後還未執行onStop(),onDestory()方法,即廣播仍還未註銷,從而導致內存泄露。
  3. 但是,onPause()一定會被執行,從而保證了廣播在App死亡前一定會被註銷,從而防止內存泄露。

3.5 兩種註冊方式區別

兩種註冊方式區別

3.3 廣播發送者向AMS發送廣播

  • 廣播 是 用”意圖(Intent)“標識
  • 定義廣播的本質 = 定義廣播所具備的“意圖(Intent)”
  • 廣播發送 = 廣播發送者 將此廣播的“意圖(Intent)”通過sendBroadcast()方法發送出去

3.3.1 廣播類型

  • 普通廣播(Normal Broadcast)
  • 系統廣播(System Broadcast)
  • 有序廣播(Ordered Broadcast)
  • 應用內廣播(Local Broadcast)

3.3.1.1 普通廣播(Normal Broadcast)

靜態註冊,顯式廣播
接收方--BroadcastReceiver類:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"got broadcast",Toast.LENGTH_LONG).show();
    }
}
接收--manifest:

<receiver
    android:name="com.example.receiveboradcast.MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.receiveboradcast.interprocess"/>
    </intent-filter>
</receiver>
發送--MainActivity:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton_1 = findViewById(R.id.Button_1);
        mButton_1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.receiveboradcast.interprocess");
                intent.setComponent(new ComponentName("com.example.receiveboradcast","com.example.receiveboradcast.MyReceiver"));
                sendBroadcast(intent);

            }
        });
    }

返回按鍵,也可以接收廣播,殺後臺不行。

動態註冊,隱式廣播
接收--MainActivity

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.example.receiveboradcast.interprocess");
    registerReceiver(myReceiver,intentFilter);
    Log.d(TAG, "onCreate: ");
}


    protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(myReceiver);
    Log.d(TAG, "onDestroy: ");
}

3.3.1.2 系統廣播

  • 當使用系統廣播時,只需要在註冊廣播接收者時定義相關的action即可,並不需要手動發送廣播,當系統有相關操作時會自動進行系統廣播。
  • Android中內置了多個系統廣播:只要涉及到手機的基本操作(如開機、網絡狀態變化、拍照等等),都會發出相應的廣播
  • 每個廣播都有特定的Intent - Filter
  • Android 8.1之後系統廣播權限修改:frameworks/base/core/res/AndroidManifest.xml
// Android 8.0 上不限制的隱式廣播
/**
開機廣播
 Intent.ACTION_LOCKED_BOOT_COMPLETED
 Intent.ACTION_BOOT_COMPLETED
*/
"保留原因:這些廣播只在首次啓動時發送一次,並且許多應用都需要接收此廣播以便進行作業、鬧鈴等事項的安排。"

/**
增刪用戶
Intent.ACTION_USER_INITIALIZE
"android.intent.action.USER_ADDED"
"android.intent.action.USER_REMOVED"
*/
"保留原因:這些廣播只有擁有特定系統權限的app才能監聽,因此大多數正常應用都無法接收它們。"
    
/**
時區、ALARM變化
"android.intent.action.TIME_SET"
Intent.ACTION_TIMEZONE_CHANGED
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED
*/
"保留原因:時鐘應用可能需要接收這些廣播,以便在時間或時區變化時更新鬧鈴"

/**
語言區域變化
Intent.ACTION_LOCALE_CHANGED
*/
"保留原因:只在語言區域發生變化時發送,並不頻繁。 應用可能需要在語言區域發生變化時更新其數據。"

/**
Usb相關
UsbManager.ACTION_USB_ACCESSORY_ATTACHED
UsbManager.ACTION_USB_ACCESSORY_DETACHED
UsbManager.ACTION_USB_DEVICE_ATTACHED
UsbManager.ACTION_USB_DEVICE_DETACHED
*/
"保留原因:如果應用需要了解這些 USB 相關事件的信息,目前尚未找到能夠替代註冊廣播的可行方案"

/**
藍牙狀態相關
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED
BluetoothDevice.ACTION_ACL_CONNECTED
BluetoothDevice.ACTION_ACL_DISCONNECTED
*/
"保留原因:應用接收這些藍牙事件的廣播時不太可能會影響用戶體驗"

/**
Telephony相關
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED
TelephonyIntents.SECRET_CODE_ACTION
TelephonyManager.ACTION_PHONE_STATE_CHANGED
TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED
TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED
*/
"保留原因:設備製造商 (OEM) 電話應用可能需要接收這些廣播"

/**
賬號相關
AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION
*/
"保留原因:一些應用需要了解登錄帳號的變化,以便爲新帳號和變化的帳號設置計劃操作"

/**
應用數據清除
Intent.ACTION_PACKAGE_DATA_CLEARED
*/
"保留原因:只在用戶顯式地從 Settings 清除其數據時發送,因此廣播接收器不太可能嚴重影響用戶體驗"
    
/**
軟件包被移除
Intent.ACTION_PACKAGE_FULLY_REMOVED
*/
"保留原因:一些應用可能需要在另一軟件包被移除時更新其存儲的數據;對於這些應用,尚未找到能夠替代註冊此廣播的可行方案"

/**
外撥電話
Intent.ACTION_NEW_OUTGOING_CALL
*/
"保留原因:執行操作來響應用戶打電話行爲的應用需要接收此廣播"
    
/**
當設備所有者被設置、改變或清除時發出
DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED
*/
"保留原因:此廣播發送得不是很頻繁;一些應用需要接收它,以便知曉設備的安全狀態發生了變化"
    
/**
日曆相關
CalendarContract.ACTION_EVENT_REMINDER
*/
"保留原因:由日曆provider發送,用於向日歷應用發佈事件提醒。因爲日曆provider不清楚日曆應用是什麼,所以此廣播必須是隱式廣播。"
    
/**
安裝或移除存儲相關廣播
Intent.ACTION_MEDIA_MOUNTED
Intent.ACTION_MEDIA_CHECKING
Intent.ACTION_MEDIA_EJECT
Intent.ACTION_MEDIA_UNMOUNTED
Intent.ACTION_MEDIA_UNMOUNTABLE
Intent.ACTION_MEDIA_REMOVED
Intent.ACTION_MEDIA_BAD_REMOVAL
*/
"保留原因:這些廣播是作爲用戶與設備進行物理交互的結果:安裝或移除存儲卷或當啓動初始化時(當可用卷被裝載)的一部分發送的,因此它們不是很常見,並且通常是在用戶的掌控下"

/**
短信、WAP PUSH相關
Telephony.Sms.Intents.SMS_RECEIVED_ACTION
Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION

注意:需要申請以下權限纔可以接收
"android.permission.RECEIVE_SMS"
"android.permission.RECEIVE_WAP_PUSH"
*/
"保留原因:SMS短信應用需要接收這些廣播"

3.3.1.3 有序廣播

參考:
Android的有序廣播和無序廣播(解決安卓8.0版本之後有序廣播的接收問題)
Android中的有序和無序廣播淺析

發佈了44 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章