首先寫一個類要繼承BroadcastReceiver\x0d\x0a第一種:在清單文件中聲明,添加\x0d\x0a\x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a第二種使用代碼進(jìn)行注冊如:\x0d\x0aIntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");\x0d\x0aIncomingSMSReceiver receiver = new IncomgSMSReceiver();\x0d\x0aregisterReceiver(receiver.filter);
創(chuàng)新互聯(lián)建站擁有十多年成都網(wǎng)站建設(shè)工作經(jīng)驗,為各大企業(yè)提供成都網(wǎng)站設(shè)計、網(wǎng)站制作服務(wù),對于網(wǎng)頁設(shè)計、PC網(wǎng)站建設(shè)(電腦版網(wǎng)站建設(shè))、重慶APP開發(fā)、wap網(wǎng)站建設(shè)(手機(jī)版網(wǎng)站建設(shè))、程序開發(fā)、網(wǎng)站優(yōu)化(SEO優(yōu)化)、微網(wǎng)站、國際域名空間等,憑借多年來在互聯(lián)網(wǎng)的打拼,我們在互聯(lián)網(wǎng)網(wǎng)站建設(shè)行業(yè)積累了很多網(wǎng)站制作、網(wǎng)站設(shè)計、網(wǎng)絡(luò)營銷經(jīng)驗,集策劃、開發(fā)、設(shè)計、營銷、管理等網(wǎng)站化運(yùn)作于一體,具備承接各種規(guī)模類型的網(wǎng)站建設(shè)項目的能力。
BroadcastReceiver是Android四大組件之一,主要用于接收系統(tǒng)廣播和其他應(yīng)用程序的廣播。
BroadcastReceiver的使用非常簡單
寫一個類繼承BroadcastReceiver
注意onReceive是主線程不要做耗時操作否則阻塞10s會ANR
注冊廣播(靜態(tài)注冊)
動態(tài)注冊
動態(tài)注冊的廣播一定要再適當(dāng)?shù)臅r機(jī)調(diào)用 unregisterReceiver();解除注冊。
發(fā)送廣播
兩種注冊方式的區(qū)別
1.動態(tài)注冊的廣播是非常駐型廣播,此時廣播是跟隨宿主的生命周期的,宿主不在了廣播也就不在了。
2.靜態(tài)注冊的廣播是常駐型廣播,即應(yīng)用程序關(guān)閉后,依然能夠收到廣播。
以下廣播簡稱Broadcast
?? 是Android四大組件之一,在四大組件的另外兩個組件 和 擁有發(fā)送和接收廣播的能力。Android 是在 進(jìn)程間通信機(jī)制的基礎(chǔ)上實現(xiàn)的,內(nèi)部基于消息發(fā)布和訂閱的事件驅(qū)動模型,廣播發(fā)送者負(fù)責(zé)發(fā)送消息,廣播接收者需要先訂閱消息,然后才能收到消息。 進(jìn)程間通信與 的區(qū)別在于:
?? 有三種類型
?? 存在一個注冊中心,也可以說是一個調(diào)度中心,即 。廣播接收者將自己注冊到 中,并指定要接收的廣播類型;廣播發(fā)送者發(fā)送廣播時,發(fā)送的廣播首先會發(fā)送到 , 根據(jù)廣播的類型找到對應(yīng)的 ,找到后邊將廣播發(fā)送給其處理。
?? 這里以普通廣播為例子, 接收者有兩種注冊方式,一種是 ,一種是 :
(廣播的發(fā)送分為 兩種,這里針對有序的廣播) 中的android:priority=""和 中的IntentFilter.setPriority(int)可以用來設(shè)置廣播接收者的優(yōu)先級,默認(rèn)都是0 , 范圍是[-1000, 1000],值越大優(yōu)先級越高,優(yōu)先級越高越早收到。
?? 在相同優(yōu)先級接收同個類型廣播時, 的廣播接收器比 的廣播接收者更快的接收到對應(yīng)的廣播,這個之后會進(jìn)行分析。
?? 注:以下源碼基于rk3399_industry Android7.1.2
?? 的流程可分為 , 和 三個部分,這里依次分析下
?? 在Android系統(tǒng)的 機(jī)制中,前面提到, 作為一個注冊和調(diào)度中心負(fù)責(zé)注冊和轉(zhuǎn)發(fā) 。所以 的注冊過程就是把它注冊到 的過程。
?? 這里我們分析 廣播的過程, 和 有一個共同的父類 ,所以它們對應(yīng)的注冊過程其實是調(diào)用 ,接下來我們按照流程逐步分析調(diào)用流程的源碼。
frameworks/base/core/java/android/content/ContextWrapper.java
?? 在之前的 Android應(yīng)用程序啟動入口ActivityThread.main流程分析 分析過,在我們啟動 Activity 時會創(chuàng)建一個 對象,然后通過 傳給我們啟動的 ,其內(nèi)部就會將該對象賦值給 ; 的 方法也是類似的賦值流程,這里放個簡易的源碼應(yīng)該更好理解
?? 可以看到最后都會將生成的 對象賦值給對應(yīng)的
對象。接下來繼續(xù)分析 , 即 函數(shù)。
/frameworks/base/core/java/android/app/ContextImpl.java
?? 這里我們首先看下如何將廣播接收者 封裝成一個 接口的 本地對象
/frameworks/base/core/java/android/app/LoadedApk.java
?? 每一個注冊過廣播接收者的 或 組件在font color='Crimson' LoadedApk /font類中都有個對應(yīng)的 對象,該對象負(fù)責(zé)將 與 組件關(guān)聯(lián)起來。這些對象,以關(guān)聯(lián)的 作為關(guān)鍵字保存在一個 中。之后對應(yīng)的 又以 的 作為關(guān)鍵字保存在 的成員變量 對象中。最后通過 對應(yīng)的 方法獲得其 接口的 本地對象。之后再回到 注冊方法內(nèi),將 對象發(fā)給 進(jìn)行注冊。
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
?? 在的 或 注冊一個 時,并不是將其注冊到font color='OrangeRed'AMS/font中,而是將與它關(guān)聯(lián)的font color='OrangeRed'InnerReceiver/font對象注冊到font color='OrangeRed'AMS/font中,當(dāng)font color='OrangeRed'AMS/font接收到廣播時,會根據(jù) 在內(nèi)部找到對應(yīng)的font color='OrangeRed'InnerReceiver/font對象,然后在通過這個對象將這個廣播發(fā)送給對應(yīng)的 處理。
?? 注冊過程這邊畫了一個簡單的流程圖:
?? font color='OrangeRed'Broadcast/font的發(fā)送過程可簡單描述為以下幾個過程:
frameworks/base/core/java/android/content/ContextWrapper.java
/frameworks/base/core/java/android/app/ContextImpl.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
廣播(Broadcast)機(jī)制用于進(jìn)程/線程間通信,廣播分為廣播發(fā)送和廣播接收兩個過程,其中廣播接收者BroadcastReceiver便是Android四大組件之一。
BroadcastReceiver分為兩類:
從廣播發(fā)送方式可分為三類:
廣播在系統(tǒng)中以BroadcastRecord對象來記錄, 該對象有幾個時間相關(guān)的成員變量.
廣播注冊,對于應(yīng)用開發(fā)來說,往往是在Activity/Service中調(diào)用 registerReceiver() 方法,而Activity或Service都間接繼承于Context抽象類,真正干活是交給ContextImpl類。另外調(diào)用getOuterContext()可獲取最外層的調(diào)用者Activity或Service。
[ContextImpl.java]
其中broadcastPermission擁有廣播的權(quán)限控制,scheduler用于指定接收到廣播時onRecive執(zhí)行線程,當(dāng)scheduler=null則默認(rèn)代表在主線程中執(zhí)行,這也是最常見的用法
[ContextImpl.java]
ActivityManagerNative.getDefault()返回的是ActivityManagerProxy對象,簡稱AMP.
該方法中參數(shù)有mMainThread.getApplicationThread()返回的是ApplicationThread,這是Binder的Bn端,用于system_server進(jìn)程與該進(jìn)程的通信。
[- LoadedApk.java]
不妨令 以BroadcastReceiver(廣播接收者)為key,LoadedApk.ReceiverDispatcher(分發(fā)者)為value的ArrayMap 記為 A 。此處 mReceivers 是一個以 Context 為key,以 A 為value的ArrayMap。對于ReceiverDispatcher(廣播分發(fā)者),當(dāng)不存在時則創(chuàng)建一個。
此處mActivityThread便是前面?zhèn)鬟f過來的當(dāng)前主線程的Handler.
ReceiverDispatcher(廣播分發(fā)者)有一個內(nèi)部類 InnerReceiver ,該類繼承于 IIntentReceiver.Stub 。顯然,這是一個Binder服務(wù)端,廣播分發(fā)者通過rd.getIIntentReceiver()可獲取該Binder服務(wù)端對象 InnerReceiver ,用于Binder IPC通信。
[- ActivityManagerNative.java]
這里有兩個Binder服務(wù)端對象 caller 和 receiver ,都代表執(zhí)行注冊廣播動作所在的進(jìn)程. AMP通過Binder驅(qū)動將這些信息發(fā)送給system_server進(jìn)程中的AMS對象,接下來進(jìn)入AMS.registerReceiver。
[- ActivityManagerService.java]
其中 mRegisteredReceivers 記錄著所有已注冊的廣播,以receiver IBinder為key, ReceiverList為value為HashMap。
在BroadcastQueue中有兩個廣播隊列mParallelBroadcasts,mOrderedBroadcasts,數(shù)據(jù)類型都為ArrayListbroadcastrecord style="box-sizing: border-box;":/broadcastrecord
mLruProcesses數(shù)據(jù)類型為 ArrayListProcessRecord ,而ProcessRecord對象有一個IApplicationThread字段,根據(jù)該字段查找出滿足條件的ProcessRecord對象。
該方法用于匹配發(fā)起的Intent數(shù)據(jù)是否匹配成功,匹配項共有4項action, type, data, category,任何一項匹配不成功都會失敗。
broadcastQueueForIntent(Intent intent)通過判斷intent.getFlags()是否包含F(xiàn)LAG_RECEIVER_FOREGROUND 來決定是前臺或后臺廣播,進(jìn)而返回相應(yīng)的廣播隊列mFgBroadcastQueue或者mBgBroadcastQueue。
注冊廣播:
另外,當(dāng)注冊的是Sticky廣播:
廣播注冊完, 另一個操作便是在廣播發(fā)送過程.
發(fā)送廣播是在Activity或Service中調(diào)用 sendBroadcast() 方法,而Activity或Service都間接繼承于Context抽象類,真正干活是交給ContextImpl類。
[ContextImpl.java]
[- ActivityManagerNative.java]
[- ActivityManagerService.java]
broadcastIntent()方法有兩個布爾參數(shù)serialized和sticky來共同決定是普通廣播,有序廣播,還是Sticky廣播,參數(shù)如下:
broadcastIntentLocked方法比較長,這里劃分為8個部分來分別說明。
這個過程最重要的工作是:
BroadcastReceiver還有其他flag,位于Intent.java常量:
主要功能:
這個過主要處于系統(tǒng)相關(guān)的10類廣播,這里不就展開講解了.
這個過程主要是將sticky廣播增加到list,并放入mStickyBroadcasts里面。
其他說明:
AMS.collectReceiverComponents :
廣播隊列中有一個成員變量 mParallelBroadcasts ,類型為ArrayListbroadcastrecord style="box-sizing: border-box;",記錄著所有的并行廣播。/broadcastrecord
動態(tài)注冊的registeredReceivers,全部合并都receivers,再統(tǒng)一按串行方式處理。
廣播隊列中有一個成員變量 mOrderedBroadcasts ,類型為ArrayListbroadcastrecord style="box-sizing: border-box;",記錄著所有的有序廣播。/broadcastrecord
發(fā)送廣播過程:
處理方式:
可見不管哪種廣播方式,都是通過broadcastQueueForIntent()來根據(jù)intent的flag來判斷前臺隊列或者后臺隊列,然后再調(diào)用對應(yīng)廣播隊列的scheduleBroadcastsLocked方法來處理廣播;
在發(fā)送廣播過程中會執(zhí)行 scheduleBroadcastsLocked 方法來處理相關(guān)的廣播
[- BroadcastQueue.java]
在BroadcastQueue對象創(chuàng)建時,mHandler=new BroadcastHandler(handler.getLooper());那么此處交由mHandler的handleMessage來處理:
由此可見BroadcastHandler采用的是”ActivityManager”線程的Looper
[- BroadcastQueue.java]
此處mService為AMS,整個流程還是比較長的,全程持有AMS鎖,所以廣播效率低的情況下,直接會嚴(yán)重影響這個手機(jī)的性能與流暢度,這里應(yīng)該考慮細(xì)化同步鎖的粒度。
有兩種注冊廣播方式:
1.常駐型廣播
常駐型廣播,當(dāng)應(yīng)用程序關(guān)閉了,如果有廣播信息來,寫的廣播接收器同樣的能接收到,它的注冊方式就是在應(yīng)用程序的AndroidManifast.xml 中進(jìn)行注冊,這種注冊方式通常又被稱作靜態(tài)注冊。這種方式可以理解為通過清單文件注冊的廣播是交給操作系統(tǒng)去處理的。示例代碼如下:
AndroidManifest.xml中配置廣播
?xml version="1.0" encoding="utf-8"?
manifest xmlns:android=""
package="spl.broadCastReceiver"
android:versionCode="1"
android:versionName="1.0"
application android:icon="@drawable/icon" android:label="@string/app_name"
activity android:name=".BroadCastReceiverActivity"
android:label="@string/app_name"
intent-filter
action android:name="android.intent.action.MAIN" /
category android:name="android.intent.category.LAUNCHER" /
/intent-filter
/activity
!--廣播注冊、name里面填寫廣播類的路徑--
receiver android:name=".SmsBroadCastReceiver"
intent-filter android:priority="20"
action android:name="android.provider.Telephony.SMS_RECEIVED"/
/intent-filter
/receiver
/application
uses-sdk android:minSdkVersion="7" /
!-- 權(quán)限申請 --
uses-permission android:name="android.permission.RECEIVE_SMS"/uses-permission
/manifest
2.非常駐型廣播
非常駐型廣播,當(dāng)應(yīng)用程序結(jié)束了,廣播自然就沒有了,比如在 Activity 中的 onCreate 或者 onResume 中注冊廣播接收者,在 onDestory 中注銷廣播接收者。這樣廣播接收者就一個非常駐型的了,這種注冊方式也叫動態(tài)注冊。這種方式可以理解為通過代碼注冊的廣播是和注冊者關(guān)聯(lián)在一起的。比如寫一個監(jiān)聽 SDcard 狀態(tài)的廣播接收者:
package cn.sunzn.mosecurity.activity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Environment;
public class SDcard extends Activity {
SdcardStateChanageReceiver sdcardStateReceiver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sdcardStateReceiver = new SdcardStateChanageReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addDataScheme("file");
registerReceiver(sdcardStateReceiver, filter);
}
protected void onDestroy() {
unregisterReceiver(sdcardStateReceiver);
}
class SdcardStateChanageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
checkSDCard();
}
public void checkSDCard() {
String state = Environment.getExternalStorageState();
System.out.println(state);
if (state.equals(Environment.MEDIA_REMOVED) || state.equals(Environment.MEDIA_UNMOUNTED)) {
System.out.println("SDCard 已卸載!");
}
}
}
}
(a).動態(tài)注冊 ? ? ? ? ? ? 在UI中注冊的廣播,例如:
(b).靜態(tài)注冊 ? ? ? ? ? ??
需要在manifest中進(jìn)行注冊(在安卓8.0后系統(tǒng)廢除了大部分靜態(tài)廣播,最好使用動態(tài)注冊)。
(a).系統(tǒng)廣播 ? ? ? ? ? ??
系統(tǒng)中已經(jīng)定義的廣播,此類廣播只能由系統(tǒng)發(fā)出,并且需要在intent-filter中加上系統(tǒng)已經(jīng)寫的action。? ? ? ? ? ? ?
(b).自定義廣播 ? ? ? ??
顧名思義,是用戶自己定義的廣播。
(a)我們首先需要一個廣播接收類 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
(b)其次注冊動態(tài)廣播
(c)最后需要通過send方法發(fā)送一個廣播供廣播接收者接受
另外還有有序廣播和無序廣播,這篇博客寫的比較詳細(xì),供大家參考: Android的有序廣播和無序廣播(解決安卓8.0版本之后有序廣播的接收問題) - ming3 - 博客園 (cnblogs.com)