廣播(Broadcast)機(jī)制用于進(jìn)程/線程間通信,廣播分為廣播發(fā)送和廣播接收兩個(gè)過程,其中廣播接收者BroadcastReceiver便是Android四大組件之一。
成都創(chuàng)新互聯(lián)公司專注于納雍企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,購(gòu)物商城網(wǎng)站建設(shè)。納雍網(wǎng)站建設(shè)公司,為納雍等地區(qū)提供建站服務(wù)。全流程按需策劃,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
BroadcastReceiver分為兩類:
從廣播發(fā)送方式可分為三類:
廣播在系統(tǒng)中以BroadcastRecord對(duì)象來記錄, 該對(duì)象有幾個(gè)時(shí)間相關(guān)的成員變量.
廣播注冊(cè),對(duì)于應(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用于指定接收到廣播時(shí)onRecive執(zhí)行線程,當(dāng)scheduler=null則默認(rèn)代表在主線程中執(zhí)行,這也是最常見的用法
[ContextImpl.java]
ActivityManagerNative.getDefault()返回的是ActivityManagerProxy對(duì)象,簡(jiǎn)稱AMP.
該方法中參數(shù)有mMainThread.getApplicationThread()返回的是ApplicationThread,這是Binder的Bn端,用于system_server進(jìn)程與該進(jìn)程的通信。
[- LoadedApk.java]
不妨令 以BroadcastReceiver(廣播接收者)為key,LoadedApk.ReceiverDispatcher(分發(fā)者)為value的ArrayMap 記為 A 。此處 mReceivers 是一個(gè)以 Context 為key,以 A 為value的ArrayMap。對(duì)于ReceiverDispatcher(廣播分發(fā)者),當(dāng)不存在時(shí)則創(chuàng)建一個(gè)。
此處mActivityThread便是前面?zhèn)鬟f過來的當(dāng)前主線程的Handler.
ReceiverDispatcher(廣播分發(fā)者)有一個(gè)內(nèi)部類 InnerReceiver ,該類繼承于 IIntentReceiver.Stub 。顯然,這是一個(gè)Binder服務(wù)端,廣播分發(fā)者通過rd.getIIntentReceiver()可獲取該Binder服務(wù)端對(duì)象 InnerReceiver ,用于Binder IPC通信。
[- ActivityManagerNative.java]
這里有兩個(gè)Binder服務(wù)端對(duì)象 caller 和 receiver ,都代表執(zhí)行注冊(cè)廣播動(dòng)作所在的進(jìn)程. AMP通過Binder驅(qū)動(dòng)將這些信息發(fā)送給system_server進(jìn)程中的AMS對(duì)象,接下來進(jìn)入AMS.registerReceiver。
[- ActivityManagerService.java]
其中 mRegisteredReceivers 記錄著所有已注冊(cè)的廣播,以receiver IBinder為key, ReceiverList為value為HashMap。
在BroadcastQueue中有兩個(gè)廣播隊(duì)列mParallelBroadcasts,mOrderedBroadcasts,數(shù)據(jù)類型都為ArrayListbroadcastrecord style="box-sizing: border-box;":/broadcastrecord
mLruProcesses數(shù)據(jù)類型為 ArrayListProcessRecord ,而ProcessRecord對(duì)象有一個(gè)IApplicationThread字段,根據(jù)該字段查找出滿足條件的ProcessRecord對(duì)象。
該方法用于匹配發(fā)起的Intent數(shù)據(jù)是否匹配成功,匹配項(xiàng)共有4項(xiàng)action, type, data, category,任何一項(xiàng)匹配不成功都會(huì)失敗。
broadcastQueueForIntent(Intent intent)通過判斷intent.getFlags()是否包含F(xiàn)LAG_RECEIVER_FOREGROUND 來決定是前臺(tái)或后臺(tái)廣播,進(jìn)而返回相應(yīng)的廣播隊(duì)列mFgBroadcastQueue或者mBgBroadcastQueue。
注冊(cè)廣播:
另外,當(dāng)注冊(cè)的是Sticky廣播:
廣播注冊(cè)完, 另一個(gè)操作便是在廣播發(fā)送過程.
發(fā)送廣播是在Activity或Service中調(diào)用 sendBroadcast() 方法,而Activity或Service都間接繼承于Context抽象類,真正干活是交給ContextImpl類。
[ContextImpl.java]
[- ActivityManagerNative.java]
[- ActivityManagerService.java]
broadcastIntent()方法有兩個(gè)布爾參數(shù)serialized和sticky來共同決定是普通廣播,有序廣播,還是Sticky廣播,參數(shù)如下:
broadcastIntentLocked方法比較長(zhǎng),這里劃分為8個(gè)部分來分別說明。
這個(gè)過程最重要的工作是:
BroadcastReceiver還有其他flag,位于Intent.java常量:
主要功能:
這個(gè)過主要處于系統(tǒng)相關(guān)的10類廣播,這里不就展開講解了.
這個(gè)過程主要是將sticky廣播增加到list,并放入mStickyBroadcasts里面。
其他說明:
AMS.collectReceiverComponents :
廣播隊(duì)列中有一個(gè)成員變量 mParallelBroadcasts ,類型為ArrayListbroadcastrecord style="box-sizing: border-box;",記錄著所有的并行廣播。/broadcastrecord
動(dòng)態(tài)注冊(cè)的registeredReceivers,全部合并都receivers,再統(tǒng)一按串行方式處理。
廣播隊(duì)列中有一個(gè)成員變量 mOrderedBroadcasts ,類型為ArrayListbroadcastrecord style="box-sizing: border-box;",記錄著所有的有序廣播。/broadcastrecord
發(fā)送廣播過程:
處理方式:
可見不管哪種廣播方式,都是通過broadcastQueueForIntent()來根據(jù)intent的flag來判斷前臺(tái)隊(duì)列或者后臺(tái)隊(duì)列,然后再調(diào)用對(duì)應(yīng)廣播隊(duì)列的scheduleBroadcastsLocked方法來處理廣播;
在發(fā)送廣播過程中會(huì)執(zhí)行 scheduleBroadcastsLocked 方法來處理相關(guān)的廣播
[- BroadcastQueue.java]
在BroadcastQueue對(duì)象創(chuàng)建時(shí),mHandler=new BroadcastHandler(handler.getLooper());那么此處交由mHandler的handleMessage來處理:
由此可見BroadcastHandler采用的是”ActivityManager”線程的Looper
[- BroadcastQueue.java]
此處mService為AMS,整個(gè)流程還是比較長(zhǎng)的,全程持有AMS鎖,所以廣播效率低的情況下,直接會(huì)嚴(yán)重影響這個(gè)手機(jī)的性能與流暢度,這里應(yīng)該考慮細(xì)化同步鎖的粒度。
為了解決廣播的安全性問題,Android引入了本地廣播機(jī)制,使用該機(jī)制發(fā)出的廣播只能在應(yīng)用程序的內(nèi)部進(jìn)行傳遞,并且廣播接收器也只能接收來自本應(yīng)用程序發(fā)出的廣播。
本地廣播是無法通過靜態(tài)注冊(cè)的方式來接收的。我們知道靜態(tài)注冊(cè)主要是為了在程序未啟動(dòng)的情況下能接收廣播,而當(dāng)我們發(fā)送本地廣播的時(shí)候,程序肯定是已經(jīng)啟動(dòng)的了,所以我們需要?jiǎng)討B(tài)注冊(cè)方式創(chuàng)建接收器。
在這里我們創(chuàng)建一個(gè)繼承于BroadcastReceiver的類LocalReceiver。onReceive()處理你接收到的廣播內(nèi)容,在這里我用Toast來創(chuàng)建一個(gè)提示接收到消息的彈窗
在activity_main.xml文件創(chuàng)建一個(gè)用于發(fā)送廣播的按鈕
首先通過本地廣播管理器LocalBroadcastManager的getInstance()方法獲取一個(gè)實(shí)例,并分別創(chuàng)建過濾器IntentFilter和自定義接收器LocalReceiver的實(shí)例。給IntentFilter的實(shí)例添加一個(gè)action:localbroadcast(接收的廣播的名稱),然后調(diào)用LocalBroadcastManager的registerReceiver()方法進(jìn)行注冊(cè),并將LocalReceiver的實(shí)例和IntentFilter的實(shí)例都傳進(jìn)去。這樣本地監(jiān)聽器就創(chuàng)建完成了。
調(diào)用LocalBroadcastManager的sendBroadcast()發(fā)送本地廣播。運(yùn)行程序,點(diǎn)擊Send Button按鈕,我們可以看到彈窗顯示“This is in LocalReceiver”,說明本地廣播發(fā)送和接收成功了。
當(dāng)然,我們最后一定不要忘了取消注冊(cè)。我們可以通過調(diào)用unregisterReceiver()方法來實(shí)現(xiàn)。至此,Android的標(biāo)準(zhǔn)廣播發(fā)送就完成了。
1.發(fā)送的廣播只能在本程序內(nèi)傳遞,不必?fù)?dān)心數(shù)據(jù)泄露
2.其它程序廣播無法發(fā)送到本程序的內(nèi)部,不必?fù)?dān)心安全漏洞隱患
3.本地廣播比系統(tǒng)全局廣播更加高效
對(duì)于Activity的啟動(dòng)流程,我們已經(jīng)有了幾個(gè)版本的分析了。這里我們分析一個(gè)更容易一些的,四大組件中最簡(jiǎn)單的Broadcast Receiver。
關(guān)于Broadcast,有幾點(diǎn)需要了解。首先是廣播的類型,然后是廣播的發(fā)送方法,最后是廣播是如何被接收的。這三者相輔相承的,比如普通廣播和有序廣播只有在詳細(xì)了解了廣播的接收過程了之后,才能真正明白它的含義。
普通的廣播是不在意順序的,最簡(jiǎn)單的理解是同時(shí)可以收到這個(gè)廣播。如果應(yīng)用是動(dòng)態(tài)注冊(cè)這個(gè)廣播的,且廣播發(fā)送時(shí)這個(gè)進(jìn)程還活著,那么當(dāng)然可以并發(fā)的把廣播盡快地傳送出去是最好的。
但是,如果是通過AndroidManifest.xml靜態(tài)注冊(cè)的情況,也就是說這個(gè)廣播首先要把一個(gè)進(jìn)程啟動(dòng)起來,這時(shí)并發(fā)啟動(dòng)很多進(jìn)程就是個(gè)問題了。Android目前的做法是,對(duì)這種靜態(tài)的廣播接收者,自動(dòng)按有序廣播的方式來串行處理。但是這對(duì)應(yīng)用是透明的,應(yīng)用不能假設(shè)系統(tǒng)已經(jīng)把靜態(tài)的無序廣播當(dāng)成有序廣播來處理。
這個(gè)時(shí)候講粘性廣播有福了,因?yàn)閺腁ndroid 5.0(API 21)開始,因?yàn)榘踩缘膯栴},官方已經(jīng)正式廢棄了粘性廣播。
Context類提供兩個(gè)方法可以用于發(fā)送普通廣播:
差別是第二個(gè)設(shè)置權(quán)限。
發(fā)給特定的用戶:
有序廣播因?yàn)橐幚硐⒌奶幚斫Y(jié)果,所以要復(fù)雜一些。
如果只是想讓廣播可以按優(yōu)先級(jí)來收取,并不在意處理的結(jié)果,可以用下面的版本:
同樣,在多用戶環(huán)境下,也可以選擇給哪個(gè)用戶發(fā)廣播:
不管是普通的還是有序的廣播都對(duì)應(yīng)有粘性的版本:
以上的API都是定義于Context類中:
首先我們先看看發(fā)送端是如何發(fā)送的。
我們首先先放一個(gè)大圖,讓大家先有一個(gè)直觀的印象,不管普通廣播、有序廣播、粘性廣播如何組合,最終都匯集到一個(gè)大方法中。
我們先看應(yīng)用發(fā)送普通廣播的一個(gè)簡(jiǎn)單的例子:
非常簡(jiǎn)單,調(diào)用ContentWrapper的sendBroadcast方法就可以了。
然后我們順藤摸瓜就好了。
Activity中的sendBroadcast,實(shí)際上調(diào)用的是:
我們來看frameworks/base/core/java/android/content/ContextWrapper.java中對(duì)sendBroadcast的定義:
ContextWrapper只是一個(gè)包裝,真正的實(shí)現(xiàn)在ContextImpl中
我們來看/frameworks/base/core/java/android/app/ContextImpl.java中真正實(shí)現(xiàn)sendBroadcast的功能:
它會(huì)通過IPC去調(diào)用AMS的broadcastIntent。由于我們這個(gè)普通的廣播的方法參數(shù)最少,所以好多都是傳null。
加鎖,定參數(shù),然后調(diào)用真正的邏輯的實(shí)現(xiàn)。
我們先把broadcastIntentLocked的真正邏輯放一下,先看看有序廣播是如何發(fā)送的。
ContextWrapper.sendOrderedBroadcast
Context是abstract方法,調(diào)用的是ContextWrapper的實(shí)現(xiàn):
跟普通廣播一樣,還是會(huì)調(diào)用到ContextImpl.sendOrderedBroadcast
有序廣播調(diào)用broadcastIntent的區(qū)別在于serialized參數(shù),普通廣播為false,有序廣播為true.
原型為:
前面講過帶有回調(diào)的版本,我們看看它是如何實(shí)現(xiàn)的:
當(dāng)然還是調(diào)用ContextImpl.sendOrderedBroadcast
這次變成只是一個(gè)封裝了,它會(huì)調(diào)用一個(gè)更多參數(shù)的版本:
這次是一個(gè)全參數(shù)調(diào)用broadcastIntent的版本了,除了sticky就齊了
我們也不繞圈子了,直接看ContextImpl.sendStickyBroadcast.
起一個(gè)線程,每發(fā)一個(gè)廣播后就sleep一分鐘,如此循環(huán)。(或者接受系統(tǒng)的timechanged這個(gè)廣播,這個(gè)廣播好像一分鐘發(fā)一次)。
Android 在發(fā)送廣播時(shí)的方法 sendBroadcast(Intent)。
①:Intent myIntent = new Intent();——【創(chuàng)建Intent對(duì)象】
②:myIntent.setAction(String)——【設(shè)置一般的要執(zhí)行的動(dòng)作。參數(shù):動(dòng)作一個(gè)動(dòng)作的名稱,如ACTION_VIEW。應(yīng)用程序的具體行動(dòng),應(yīng)與供應(yīng)商的包名作為前綴?!?/p>
③:myIntent.putExtra(String,Object)——【廣播中額外發(fā)送的數(shù)據(jù),String為自定義key,Object表示多種數(shù)據(jù)類型】
④:sendBroadcast(myIntent);——【發(fā)送廣播】
接收廣播
Android在接收廣播的方法是注冊(cè)一個(gè)廣播接收器 registerReceiver(MyReceiver,IntentFilter)。
①:首先創(chuàng)建MyReceiver類(類名自定義) 繼承 BroadcastReceiver類?!緞?chuàng)建廣播接收器】
②:在MyReceiver中重寫public void onReceive(Context context, Intent intent)方法。這個(gè)方法在接收到廣播后觸發(fā)。——【重寫處理方法】
③:在Activity或者Service啟動(dòng)時(shí) onCreate()、onStartCommand()等方法中實(shí)例化 MyReceiver類——【啟動(dòng)時(shí)實(shí)例化廣播接收器】
④:IntentFilter filter = new IntentFilter();——【創(chuàng)建IntentFilter對(duì)象 意圖過濾器】
⑤:filter.addAction(String);——【在過濾器中加入過濾條件,說明接收什么廣播】
⑥:registerReceiver(cmdReceiver, filter);——【注冊(cè)廣播,參數(shù)為(廣播接收器,意圖過濾器)】
以下廣播簡(jiǎn)稱Broadcast
?? 是Android四大組件之一,在四大組件的另外兩個(gè)組件 和 擁有發(fā)送和接收廣播的能力。Android 是在 進(jìn)程間通信機(jī)制的基礎(chǔ)上實(shí)現(xiàn)的,內(nèi)部基于消息發(fā)布和訂閱的事件驅(qū)動(dòng)模型,廣播發(fā)送者負(fù)責(zé)發(fā)送消息,廣播接收者需要先訂閱消息,然后才能收到消息。 進(jìn)程間通信與 的區(qū)別在于:
?? 有三種類型
?? 存在一個(gè)注冊(cè)中心,也可以說是一個(gè)調(diào)度中心,即 。廣播接收者將自己注冊(cè)到 中,并指定要接收的廣播類型;廣播發(fā)送者發(fā)送廣播時(shí),發(fā)送的廣播首先會(huì)發(fā)送到 , 根據(jù)廣播的類型找到對(duì)應(yīng)的 ,找到后邊將廣播發(fā)送給其處理。
?? 這里以普通廣播為例子, 接收者有兩種注冊(cè)方式,一種是 ,一種是 :
(廣播的發(fā)送分為 兩種,這里針對(duì)有序的廣播) 中的android:priority=""和 中的IntentFilter.setPriority(int)可以用來設(shè)置廣播接收者的優(yōu)先級(jí),默認(rèn)都是0 , 范圍是[-1000, 1000],值越大優(yōu)先級(jí)越高,優(yōu)先級(jí)越高越早收到。
?? 在相同優(yōu)先級(jí)接收同個(gè)類型廣播時(shí), 的廣播接收器比 的廣播接收者更快的接收到對(duì)應(yīng)的廣播,這個(gè)之后會(huì)進(jìn)行分析。
?? 注:以下源碼基于rk3399_industry Android7.1.2
?? 的流程可分為 , 和 三個(gè)部分,這里依次分析下
?? 在Android系統(tǒng)的 機(jī)制中,前面提到, 作為一個(gè)注冊(cè)和調(diào)度中心負(fù)責(zé)注冊(cè)和轉(zhuǎn)發(fā) 。所以 的注冊(cè)過程就是把它注冊(cè)到 的過程。
?? 這里我們分析 廣播的過程, 和 有一個(gè)共同的父類 ,所以它們對(duì)應(yīng)的注冊(cè)過程其實(shí)是調(diào)用 ,接下來我們按照流程逐步分析調(diào)用流程的源碼。
frameworks/base/core/java/android/content/ContextWrapper.java
?? 在之前的 Android應(yīng)用程序啟動(dòng)入口ActivityThread.main流程分析 分析過,在我們啟動(dòng) Activity 時(shí)會(huì)創(chuàng)建一個(gè) 對(duì)象,然后通過 傳給我們啟動(dòng)的 ,其內(nèi)部就會(huì)將該對(duì)象賦值給 ; 的 方法也是類似的賦值流程,這里放個(gè)簡(jiǎn)易的源碼應(yīng)該更好理解
?? 可以看到最后都會(huì)將生成的 對(duì)象賦值給對(duì)應(yīng)的
對(duì)象。接下來繼續(xù)分析 , 即 函數(shù)。
/frameworks/base/core/java/android/app/ContextImpl.java
?? 這里我們首先看下如何將廣播接收者 封裝成一個(gè) 接口的 本地對(duì)象
/frameworks/base/core/java/android/app/LoadedApk.java
?? 每一個(gè)注冊(cè)過廣播接收者的 或 組件在font color='Crimson' LoadedApk /font類中都有個(gè)對(duì)應(yīng)的 對(duì)象,該對(duì)象負(fù)責(zé)將 與 組件關(guān)聯(lián)起來。這些對(duì)象,以關(guān)聯(lián)的 作為關(guān)鍵字保存在一個(gè) 中。之后對(duì)應(yīng)的 又以 的 作為關(guān)鍵字保存在 的成員變量 對(duì)象中。最后通過 對(duì)應(yīng)的 方法獲得其 接口的 本地對(duì)象。之后再回到 注冊(cè)方法內(nèi),將 對(duì)象發(fā)給 進(jìn)行注冊(cè)。
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
?? 在的 或 注冊(cè)一個(gè) 時(shí),并不是將其注冊(cè)到font color='OrangeRed'AMS/font中,而是將與它關(guān)聯(lián)的font color='OrangeRed'InnerReceiver/font對(duì)象注冊(cè)到font color='OrangeRed'AMS/font中,當(dāng)font color='OrangeRed'AMS/font接收到廣播時(shí),會(huì)根據(jù) 在內(nèi)部找到對(duì)應(yīng)的font color='OrangeRed'InnerReceiver/font對(duì)象,然后在通過這個(gè)對(duì)象將這個(gè)廣播發(fā)送給對(duì)應(yīng)的 處理。
?? 注冊(cè)過程這邊畫了一個(gè)簡(jiǎn)單的流程圖:
?? font color='OrangeRed'Broadcast/font的發(fā)送過程可簡(jiǎn)單描述為以下幾個(gè)過程:
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
首先要聲明廣播
private?BroadcastReceiver?mBroadcastReceiver?=?new?BroadcastReceiver()
{
@Override
public?void?onReceive(Context?context,?Intent?intent)?//onReceive函數(shù)不能做耗時(shí)的事情,參考值:10s以內(nèi)
{
Log.d("scott",?"on?receive?action="+intent.getAction());
String?action?=?intent.getAction();
if?(action.equals("com.scott.sayhi"))
{
showDialog("on?receive?action="+intent.getAction());
}
}
};
2.其次要注冊(cè)廣播,有兩種方式:xml注冊(cè)和代碼注冊(cè)
xml注冊(cè):
receiver android:name="com.scott.sayhi.MyBroadcastReceiver"
intent-filter
action android:name="com.scott.sayhi" /
/intent-filter
/receiver
代碼注冊(cè):
IntentFilter filter = new IntentFilter();
filter.addAction("com.scott.sayhi");
MyActivity.this.registerReceiver(mBroadcastReceiver, filter);
上述2個(gè)步驟就可以了。
3.發(fā)送廣播
Intent?intent?=?new?Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("com.scott.sayhi");
MyActivity.this.sendBroadcast(intent);
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("com.scott.sayhi");
MyActivity.this.sendBroadcast(intent);
4.收聽開機(jī)廣播
intent-filter設(shè)置如下即可
intent-filter
action android:name="android.intent.action.BOOT_COMPLETED" /
category android:name="android.intent.category.HOME" /
/intent-filter
標(biāo)題名稱:發(fā)送廣播android,發(fā)送廣播分組
網(wǎng)站URL:http://chinadenli.net/article2/dsdioic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、小程序開發(fā)、標(biāo)簽優(yōu)化、營(yíng)銷型網(wǎng)站建設(shè)、面包屑導(dǎo)航、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)