天天看點

Android高頻面試專題 - 基礎篇(四)BroadcastReceiver

Android高頻面試專題 - 基礎篇(四)BroadcastReceiver

1、廣播實作原理

Android 中的廣播使用了設計模式中的觀察者模式:基于消息的釋出/訂閱事件模型。

模型中有3個角色:1. 消息訂閱者(廣播接收者) 2. 消息釋出者(廣播釋出者) 3. 消息中心( AMS ,即 Activity Manager Service )

Android高頻面試專題 - 基礎篇(四)BroadcastReceiver

原理描述:

1. 廣播接收者 通過 Binder 機制在 AMS 注冊

2. 廣播發送者 通過 Binder 機制向 AMS 發送廣播

3. AMS 根據 廣播發送者 要求,在已注冊清單中,尋找合适的廣播接收者

尋找依據:IntentFilter / Permission

4. AMS 将廣播發送到合适的廣播接收者相應的消息循環隊列中;

5. 廣播接收者通過 消息循環 拿到此廣播,并回調 onReceive()

特别注意:廣播發送者 和 廣播接收者的執行 是 異步的,發出去的廣播不會關心有無接收者接收,也不确定接收者到底是何時才能接收到;

2、廣播的分類

  • 無序廣播

也就是最常見的标準廣播,通過SendBroadcast()方法發送。

  • 有序廣播

針對廣播接收方而言,通過sendOrderedBroadcast(intent)發送,發送出去的廣播被廣播接收者按照優先級先後順序接收,相同優先級的動态注冊的廣播優先,每次隻能有一個接受者收到,接受者收到廣播後,可以通過setResultData來傳遞資料給下一個接收者,也可以通過abortBroadcast()來終止廣播繼續向下傳遞。

  • 粘性廣播

調用SendStickyBroadcast()方法發送,需要android.Manifest.permission.BROADCAST_STICKT權限,注冊者可以接受到注冊廣播前發送者發送的最後一次廣播。目前API 21中已标記為Deprecated,不推薦使用。系統中電量的廣播就是使用粘性廣播發送的。

  • 本地廣播

通過系統LocalBroadcastManager發送,隻能在目前應用内接收。相對于其他類型廣播而言,安全性高&效率高。本地廣播隻能通過LocalBroadcastManager動态注冊。

  • 系統廣播

有的地方把這個也算一個分類,這裡也提一下,系統廣播就是Android系統内置的廣播,用來通知應用一些系統狀态的改變,如:息屏亮屏,電量變化,網絡狀态變化。使用者隻需注冊對應的Action, 系統有相關操作時會自動廣播。

3、廣播注冊的方式

  • 靜态注冊

在Manifest檔案中,通過xml标簽注冊。

<receiver android:name=".XXXReceiver" android:exported="true">  <intent-filter>    <action android:name="android.intent.action.BOOT_COMPLETED" />  </intent-filter></receiver>           

複制

exported屬性表示是否暴露給其他應用,設定為true, 則可以接收到其他應用發送的廣播,預設值是由BroadcastReceiver中有無Intent-filter決定的,如果有Intent-filter,預設值為true,否則為false。

上面例子中,當此App首次啟動時,系統會自動執行個體化XXXReceiver類,并注冊到系統中。

  • 動态注冊

在代碼中通過調用Context的registerReceiver()方法進行動态注冊

@Override protected void onResume() {  super.onResume();  //執行個體化BroadcastReceiver子類 & IntentFilter  mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();  IntentFilter intentFilter = new IntentFilter(); //設定接收廣播的類型   intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE) ;   //調用Context的registerReceiver()方法進行動态注冊   registerReceiver(mBroadcastReceiver, intentFilter); }           

複制

動态注冊時要注意注冊與反注冊成對出現,否則會造成記憶體洩漏。

4、兩種廣播注冊方式的比較

Android高頻面試專題 - 基礎篇(四)BroadcastReceiver

5、LocalBroadcastManager實作原理

LocalBroadcastManager雖然使用和普通廣播沒有太大差别,但是原理卻是完全不同。LocalBroadcastManager内部維護了mReceivers和mActions兩個HashMap,

mReceivers 是接收器和IntentFilter的對應表,主要作用是友善在unregisterReceiver(…)取消注冊,同時作為對象鎖限制注冊接收器、發送廣播、取消接收器注冊等幾個過程的并發通路。

mActions 以Action為 key,注冊這個Action的BroadcastReceiver連結清單為 value。mActions 的主要作用是友善在廣播發送後快速得到可以接收它的BroadcastReceiver。

在注冊廣播時,其實是在更新這兩個Map.

HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();HashMap<String, ArrayList<ReceiverRecord>> mActions            = new HashMap<String, ArrayList<ReceiverRecord>>();           

複制

發送廣播時,根據Action從mActions中取出ReceiverRecord清單,找出action比對的廣播,然後通過Handler發送消息,在Handler的handleMessage中,取出比對的廣播清單,依次回調onReceive方法。

6、常見系統廣播

監聽網絡變化 android.net.conn.CONNECTIVITY_CHANGE
電量發生變化 ACTION_BATTERY_CHANGED
成功安裝APK ACTION_PACKAGE_ADDED
息屏/亮屏 ACTION_SCREEN_OFF/ON
系統啟動完成 ACTION_BOOT_COMPLETED
時間變化(每分鐘1次) ACTION_TIME_TICK

從Android 7.0開始,系統不會再發送廣播ACTION_NEW_PICTURE和ACTION_NEW_VIDEO,對于廣播CONNECTIVITY_ACTION必須在代碼中使用registerReceiver方法注冊接收器,在AndroidManifest檔案中聲明接收器不起作用。

從Android 8.0開始,對于大多數系統隐式廣播,不能在AndroidManifest檔案中注冊。

7、廣播安全性

Android系統中的廣播可以跨程序直接通信,會産生以下兩個問題:

其他APP可以接收到目前APP發送的廣播,導緻資料外洩。

其他APP可以向目前APP放廣播消息,導緻APP被非法控制。

(1)發送廣播

  • 發送廣播時,增加相應的permission,用于權限驗證。
  • 在Android 4.0及以上系統中發送廣播時,可以使用setPackage()方法設定接受廣播的包名。
  • 使用本地廣播。

(2)接受廣播

  • 注冊廣播接收器時,增加相應的permission,用于權限驗證。
  • 注冊廣播接收器時,設定android:exported的值為false。
  • 使用本地廣播。

發送廣播時,如果增加了permission,那接受廣播的APP必須申請相應權限,這樣才能收到對應的廣播,反之亦然。

8、廣播中能執行耗時操作嗎?

不能,廣播接收預設是在主線程中運作,在前面Android高頻面試專題 - 進階篇(一)ANR中講過,廣播逾時是10s(前台)和60s(背景),如果耗時超過這個時間,就會抛出ANR,是以如果需要在廣播内執行耗時操作,可以在廣播内啟動一個IntentService來執行。