簡介
廣播作為Android系統(tǒng)四大組件之一舆吮,起得作用是相當大援所,安卓系統(tǒng)進程之間通信是相當頻繁庐舟,而且解耦工作是重中之重,那么作為這樣的設計初衷住拭,所以誕生了廣播挪略。我們今天就來一步步看看安卓中廣播是怎么樣進行工作的历帚。
使用
- 自定義廣播接受者
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Log.i(TAG, msg);
}
}
-
注冊廣播
- 靜態(tài)注冊
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
- 動態(tài)注冊
MyReceiver receiver = new MyReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.MY_BROADCAST"); registerReceiver(receiver, filter);
- 發(fā)送廣播
public void send(View view) {
Intent intent = new Intent("android.intent.action.MY_BROADCAST");
intent.putExtra("msg", "hello receiver.");
sendBroadcast(intent);
}
- 解除廣播
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
補充:
有序廣播的定義案例:
<receiver android:name=".FirstReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.MY_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".SecondReceiver">
<intent-filter android:priority="999">
<action android:name="android.intent.action.MY_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".ThirdReceiver">
<intent-filter android:priority="998">
<action android:name="android.intent.action.MY_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
源碼分析
注冊廣播源碼分析
我們通過ContextImpl對象最后會進入如下代碼:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
//mPackageInfo的類型為LoadedApk
if (mPackageInfo != null && context != null) {
//類型是Handler,沒有設置的時候用主線程和處理
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mPackageInfo.getReceiverDispatcher()
這個方法主要在LoadedApk的private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
根據BroadcastReceiver r
拿到LoadedApk.ReceiverDispatcher
如果沒有杠娱,則new ReceiverDispatcher
將信息封裝到ReceiverDispatcher
中然后保存到數組中挽牢,當new ReceiverDispatcher
會創(chuàng)建(mIIntentReceiver = new InnerReceiver(this, !registered);
這個是一個binder)
反正上面代碼執(zhí)行完成就要有LoadedApk.ReceiverDispatcher的創(chuàng)建,并且要調用AMS的registerReceiver()
同樣在new ReceiverDispatcher的時候就會創(chuàng)建IIntentReceiver對象墨辛,這個對象是繼承Binder的。
在使用廣播的時候趴俘,我們就會遇到一種情況睹簇,就是廣播導致的anr,由于上面handler是主線程的handler寥闪,所在在oReceiver的時候太惠,一旦超時就會引發(fā)anr問題,我們可能會想和service處理辦法一樣疲憋,由于service的onCreat也是在主線程中調用的凿渊,我們處理會啟動一個子線程,在子線程中完成我們需要的工作缚柳。同理我們可不可以在onReceive方法中啟動子線程呢埃脏?
答案是不可以!如果我們用了子線程可能會產生這樣一種情況秋忙,onReceive的生命周期很短可能我們在子線程中的工作沒有做完彩掐,AMS就銷毀進程了,這個時候就達不到我們需要的結果灰追。
所以廣播也給我們準備了一種辦法堵幽,就是PendingResult,它是定義在BroadcastReceiver中的:
public abstract class BroadcastReceiver {
private PendingResult mPendingResult;
具體的做法是:
- 先調用BroadcastReceiver的goAsync函數得到一個PendingResult對象弹澎,
- 然后將該對象放到工作線程中去釋放朴下。
這樣onReceive函數就可以立即返回而不至于阻塞主線程。
同時苦蒿,Android系統(tǒng)將保證BroadcastReceiver對應進程的生命周期殴胧,
直到工作線程處理完廣播消息后,調用PendingResult的finish函數為止佩迟。
private class MyBroadcastReceiver extends BroadcastReceiver {
..................
public void onReceive(final Context context, final Intent intent) {
//得到PendingResult
final PendingResult result = goAsync();
//放到異步線程中執(zhí)行
AsyncHandler.post(new Runnable() {
@Override
public void run() {
handleIntent(context, intent);//可進行一些耗時操作
result.finish();
}
});
}
}
final class AsyncHandler {
private static final HandlerThread sHandlerThread = new HandlerThread("AsyncHandler");
private static final Handler sHandler;
static {
sHandlerThread.start();
sHandler = new Handler(sHandlerThread.getLooper());
}
public static void post(Runnable r) {
sHandler.post(r);
}
private AsyncHandler() {}
}
細節(jié)對應到源碼
public final PendingResult goAsync() {
PendingResult res = mPendingResult;
mPendingResult = null;
return res;
}
在onReceive函數中執(zhí)行異步操作溃肪,主要目的是避免一些操作阻塞了主線程,
但整個操作仍然需要保證在10s內返回結果音五,尤其是處理有序廣播和靜態(tài)廣播時惫撰。
畢竟AMS必須要收到返回結果后,才能向下一個BroadcastReceiver發(fā)送廣播躺涝。
源碼過程
ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
首先傳遞進來的核心參數:
- BroadcastReceiver receiver
- Context context
- IntentFilter filter
此方法第一步:
- scheduler = mMainThread.getHandler();//獲取主線程
- 獲取IIntentReceiver厨钻,是將receiver封裝的對象
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
- 最后通過AMS的registerReceiver將信息注冊到AMS中
我們分析一下這個IIntentReceiver rd
到底是什么扼雏?
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
這里面的幾個核心數據結構:
- mReceivers
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers;
- map
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map;
- rd
rd = new ReceiverDispatcher(r, context, handler, instrumentation, registered);
由上面代碼可以看出來是:
- 將BroadcastReceiver(r)封裝到ReceiverDispatcher(rd)
- 將r和rd對應起來到一個map
- context和map對應起來
最后形成的效果是:
ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>
方法最后返回final IIntentReceiver.Stub mIIntentReceiver;
這個是在ReceiverDispatcher的構造中進行創(chuàng)建的,傳入的參數是ReceiverDispatcher這個對象本身夯膀,也就是說receiver诗充,context等等所有的引用都在這里面。同時它是一個Binder對象诱建,說明可以夸進程傳輸數據蝴蜓。
上面的信息說明,在getReceiverDispatcher這個方法中將receiver俺猿,context等等都保存起來茎匠,并且統(tǒng)一在LoadedApk列表中進行保存。于此同時返回一個Binder對象給調用者(注冊廣播的對象)
小節(jié):
到這里我們明白ContextImpl對象的registerReceiverInternal方法的目的是將信息保存到LoadedApk中押袍,并且生成Binder對象這個對象中存有所有recevire和context等重要引用诵冒,最后AMS就可以通過這個Binder對象的引用和當前注冊廣播的進程進行通信。
AMS.registerReceiver()
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
part-1
...............
//保存系統(tǒng)內已有的sticky廣播
ArrayList<Intent> stickyIntents = null;
...............
synchronized(this) {
if (caller != null) {
//根據IApplicationThread得到對應的進程信息
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
//拋出異常谊惭,即不允許未登記的進程注冊BroadcastReceiver
...............
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
//拋出異常汽馋,即注冊進程必須攜帶Package名稱
..................
}
} else {
.............
}
......................
//為了得到IntentFilter中定義的Action信息,先取出其Iterator
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
//可能有多個action圈盔,所以這里得到action的迭代器actions
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
//依次比較BroadcastReceiver關注的Action與Stick廣播是否一致
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
//Sticky廣播中豹芯,有BroadcastReceiver關注的
//可能有多個Intent,對應的Action相似驱敲,在此先做一個初步篩選
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
//如果stickyIntents==null告组,則創(chuàng)建對應列表
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
//將這些廣播保存到stickyIntents中
stickyIntents.addAll(intents);
}
}
}
}
}
ArrayList<Intent> allSticky = null;
//stickyIntents中保存的是action匹配的
if (stickyIntents != null) {
//用于解析intent的type
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
//此時進一步判斷Intent與BroadcastReceiver的IntentFilter是否匹配
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
第一部分主要工作是:
- 得到action的迭代器
- 得到UserAll,當前user的所有粘連廣播列表添加到sticktIntents中
- 在
stickyIntents
中進行匹配將所有對應粘連廣播的intent添加到allSticky中癌佩。
part-2
//得到粘連掛廣播中的第一個廣播
Intent sticky = allSticky != null ? allSticky.get(0) : null;
................
synchronized (this) {
..............
//一個Receiver可能監(jiān)聽多個廣播木缝,多個廣播對應的BroadcastFilter組成ReceiverList
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
//首次注冊,rl為null围辙,進入此分支
//新建BroadcastReceiver對應的ReceiverList
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//添加到注冊進程里
rl.app.receivers.add(rl);
} else {
try {
//rl監(jiān)聽receiver所在進程的死亡消息
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//保存到AMS上
mRegisteredReceivers.put(receiver.asBinder(), rl);
} ..........
............
這里牽扯到一個數據結構:
- mRegisteredReceivers
final HashMap<IBinder, ReceiverList> mRegisteredReceivers;
這個主要保存的是注冊的廣播的receiver和ReceiverList我碟,再看看ReceiverList這個數據結構
class ReceiverList extends ArrayList<BroadcastFilter>
主要是存BroadcastFilter
怎么理解呢?就是一個廣播接受者可以對應多個廣播過濾器姚建,也就是BroadcastFilter矫俺,所以就形成了HashMap<IBinder, ReceiverList>
這種結構
這種結構也就是說,一個廣播可以注冊多次掸冤,每次filter不同厘托,則會將filter添加到對應的廣播接收器中。
第二部分做的工作主要有:
將filter保存到對應的BroadcastFilter中稿湿,然后將BroadcastFilter保存到對應的ReceiverList中铅匹,也就是說一個recevier對應多個Filter。
part-3
//創(chuàng)建當前IntentFilter對應的BroadcastFilter
//AMS收到廣播后饺藤,將根據BroadcastFilter決定是否將廣播遞交給對應的BroadcastReceiver
//一個BroadcastReceiver可以對應多個IntentFilter
//這些IntentFilter對應的BroadcastFilter共用一個ReceiverList
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
................
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
//allSticky不為空包斑,說明有粘性廣播需要發(fā)送給剛注冊的BroadcastReceiver
if (allSticky != null) {
ArrayList receivers = new ArrayList();
//receivers記錄bf
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
//根據intent的flag (FLAG_RECEIVER_FOREGROUND)決定是使用gBroadcastQueue還是BgBroadcastQueue
BroadcastQueue queue = broadcastQueueForIntent(intent);
//創(chuàng)建廣播對應的BroadcastRecord
//BroadcastRecord中包含了Intent流礁,即知道要發(fā)送什么廣播;
//同時其中含有receivers罗丰,內部包含bf神帅,即知道需要將廣播發(fā)送給哪個BroadcastReceiver
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
//加入到BroadcastQueue中,存入的是并發(fā)廣播對應的隊列
queue.enqueueParallelBroadcastLocked(r);
//將發(fā)送BROADCAST_INTENT_MSG萌抵,觸發(fā)AMS發(fā)送廣播
queue.scheduleBroadcastsLocked();
}
}
}
}
將通過intent匹配的粘連廣播進行發(fā)送找御。
小節(jié)上面1.2.3部分
- 將系統(tǒng)中粘連廣播根據intent進行匹配并得到保存在粘連廣播列表
- 將傳遞進來的receiver和對應的intentfilterList對應起來保存到對應列表
- 發(fā)送匹配到的粘連廣播
這里可以看出粘連廣播的特性,就是如果注冊是廣播是粘連廣播那么就在注冊的時候就會給所有接受這個粘連廣播的接收器發(fā)送廣播绍填。