注冊方式
1.動態(tài)注冊
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(new ScreenBroadcastReceiver(), filter);
另外簸喂,注冊廣播可以通過IntentFilter的 setPriority設(shè)置廣播的優(yōu)先級悲敷。
如:
IntentFilter intentFilter = new IntentFilter(ACTION);
intentFilter.setPriority(1000);
registerReceiver(firstReceiver, intentFilter);
2.靜態(tài)注冊
<receiver android:name=".ScreenBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
也可以通過 android:priority來設(shè)置廣播的優(yōu)先級
<receiver android:name=".receiver.FirstReceivcer">
<intent-filter android:priority="1000">
<action android:name="com.jack.app.myapplication.action.ORDER_BROADCAST"></action>
</intent-filter>
</receiver>
使用
public class ScreenBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
BaseActivity.ISSCREENOFF = true;
} else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解鎖
BaseActivity.ISSCREENOFF = false;
}
}
}
無序廣播與有序廣播
- 普通廣播:異步,發(fā)出時可被所有接受者收到屿良,不可以取消
- 有序廣播:根據(jù)優(yōu)先級依次傳播,直到有接受者將其終止或所有接受者都不終止它痘昌。可以通過abortBroadcast取消它的繼續(xù)傳播(責(zé)任鏈設(shè)計模式)
有序廣播接收炬转,傳值示例
注冊略辆苔,請看上面的兩者注冊方式,F(xiàn)irstReceivcer 的優(yōu)先級是1000扼劈,SecondReceiver的優(yōu)先級是100.
public class FirstReceivcer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int limit = intent.getIntExtra("limit",0);
if (limit == 1000){
String msg = intent.getStringExtra("msg");
Toast.makeText(context,msg,Toast.LENGTH_SHORT).show();
abortBroadcast();
} else{
Bundle b = new Bundle();
b.putString("new","message from firstReceiver");
setResultExtras(b);
}
}
}
public class SecondReceivcer extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int limit = intent.getIntExtra("limit", 0);
if (limit == 100) {
String msg = intent.getStringExtra("msg");
/**
* 取得上一個Receiver增加的信息
*/
Bundle resultExtras = getResultExtras(true);
String aNew = resultExtras.getString("new");
Toast.makeText(context, "get message from first receiver:" + aNew, Toast.LENGTH_SHORT).show();
abortBroadcast();
} else {
Bundle b = new Bundle();
b.putString("new", "message from secondReceiver");
setResultExtras(b);
}
}
}
在BroadcastReceiver的intent傳遞過程中
- 發(fā)送有序廣播 sendOrderedBroadcast
Intent i = new Intent();
i.setAction(ACTION);
if (id == R.id.action_menu0){
i.putExtra("limit", 100);
i.putExtra("msg","有序廣播");
sendOrderedBroadcast(i, null);
}else if (id == R.id.action_menu1){
i.putExtra("limit", 100);
i.putExtra("msg","普通廣播");
sendBroadcast(i);
}
- 可以通過abortBroadcast取消廣播的繼續(xù)傳遞
- 在有序廣播中驻啤,可以通過
Bundle b = new Bundle();
b.putString("new","message from firstReceiver");
setResultExtras(b);
來設(shè)置加入對廣播傳遞信息的修改〖龀常可以通過getResultExtras取得上一個Receiver增加的信息街佑。
/**
* 取得上一個Receiver增加的信息
*/
Bundle resultExtras = getResultExtras(true);
String aNew = resultExtras.getString("new");
BroadcastReceiver 工作過程
靜態(tài)注冊的廣播在應(yīng)用安裝的時候由系統(tǒng)自動完成注冊,具體是由 PackageManagerService 來完成整個注冊過程捍靠。這里只分析廣播的動態(tài)注冊的流程。
注冊過程
- 通過ContextWrapper
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
mBase 是一個ContextImpl對象森逮。
- ContextImpl的registerReceiver方法
registerReceiver 又會去調(diào)用自己的 registerReceiverInternal 榨婆,ReceiverDispatcher是 LoadedApk的靜態(tài)內(nèi)部類。
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
/**
* IIntentReceiver 一個 Binder 對象褒侧,因為注冊過程是一個跨進(jìn)程通信的過程
*/
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 {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
ActivityManagerNative.getDefault()就是AMS良风。
這里關(guān)注下 ReceiverDispatcher 的 getReceiverDispatcher 實(shí)現(xiàn),其先創(chuàng)建了 ReceiverDispatcher對象rd闷供,再通過
rd.getIIntentReceiver();
返回 IIntentReceiver 對象烟央,如下:
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();
}
}
- 由于注冊廣播真正的過程是在AMS中,我們看看AMS的 registerReceiver歪脏。
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
mRegisteredReceivers.put(receiver.asBinder(), rl);
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
...
}
最終會把遠(yuǎn)程的 InnerReceiver 對象以及 IntentFilter 對象存儲起來疑俭,整個廣播的注冊就完成了。
廣播的發(fā)送和接收過程
這里只分析普通廣播的發(fā)送過程婿失。廣播的發(fā)送和接受钞艇,其本質(zhì)是一個過程的兩個階段啄寡。我們從廣播的發(fā)送說起:
Context 的 sendBroadcast 是一個抽象方法。和注冊的過程一樣哩照,ContextWrapper 的sendBroadcast同樣將事情交給了 ContextImpl來處理挺物。
1. ContextImpl 的 sendBroadcast 過程如下:
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
ContextImpl 幾乎什么事都沒有干,直接向 AMS 發(fā)起了一個異步請求用于發(fā)送廣播飘弧。
2. AMS 的 broadcastIntent 源碼如下:
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle options,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, null, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
broadcastIntent 調(diào)用了 broadcastIntentLocked 识藤。broadcastIntentLocked 代碼比較多,看起來比較復(fù)雜次伶。
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
從 Android3.1開始廣播就具有這種特性-默認(rèn)情況下廣播不會發(fā)送給已經(jīng)停止的應(yīng)用痴昧,這樣做是為了防止廣播無意間或者在不必要的時候調(diào)起已經(jīng)停止運(yùn)行的應(yīng)用。在Android3.1中為Intent添加了兩個標(biāo)記位学少,分別為
1.FLAG_EXCLUDE_STOPPED_PACKAGES
表示不包含已經(jīng)停止的應(yīng)用剪个,這個時候廣播不會發(fā)送給已經(jīng)停止的應(yīng)用
2.FLAG_INCLUDE_STOPPED_PACKAGES
表示包含已經(jīng)停止的應(yīng)用,這個時候廣播會發(fā)送給已經(jīng)停止的應(yīng)用
如果確實(shí)需要調(diào)起未啟動的應(yīng)用版确,那么只需要為廣播的 Intent 添加 FLAG_INCLUDE_STOPPED_PACKAGES 標(biāo)記即可扣囊。當(dāng) FLAG_EXCLUDE_STOPPED_PACKAGES 和 FLAG_INCLUDE_STOPPED_PACKAGES 兩種標(biāo)記并存時,以 FLAG_INCLUDE_STOPPED_PACKAGES 為準(zhǔn)绒疗。
在 broadcastIntentLocked 內(nèi)部侵歇,會根據(jù) intent-filter 查找出匹配的廣播接受者并經(jīng)過一系列條件過濾。最終會把滿足條件的廣播接受者添加到 BroadcastQueue 中吓蘑。
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
appOp, registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
3. 下面看下 BroadcastQueue 中廣播的發(fā)送過程的實(shí)現(xiàn)
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
scheduleBroadcastsLocked并沒有立即執(zhí)行發(fā)送廣播惕虑,而是發(fā)送了一個BROADCAST_INTENT_MSG類型的消息,BroadcastQueue 收到消息后會調(diào)用 BroadcastQueue 的 processNextBroadcast 方法磨镶,processNextBroadcast 方法對普通廣播的處理如下:
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
無序廣播存儲在 mParallelBroadcasts 中溃蔫,系統(tǒng)會遍歷 mParallelBroadcasts 并將其中的廣播發(fā)送給他們所有的接受者。具體通過 deliverToRegisteredReceiverLocked 實(shí)現(xiàn)琳猫,deliverToRegisteredReceiverLocked 負(fù)責(zé)講一個廣播發(fā)送給一個特定的接受者伟叛,它內(nèi)部調(diào)用了 performReceiveLocked 來完成具體的發(fā)送過程。
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
performReceiveLocked 實(shí)現(xiàn)如下
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
/**
* app.thread 仍然指 ApplicatonThread
*/
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
4. 在 ApplicationThread 的 scheduleRegisteredReceiver 中脐嫂,通過 InnerReceiver 來實(shí)現(xiàn)廣播的接收统刮。
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
通過 InnerReceiver 的performReceive方法會調(diào)用 LoadedApk.ReceiverDispatcher 的performReceive。方法如下:
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
}
在上面的代碼中账千,會創(chuàng)建一個 Args 對象侥蒙,并通過 mActivityThread 的post方法執(zhí)行Args中的邏輯,而 Args實(shí)現(xiàn)了 Runnable 接口匀奏。mActivityThread 是一個Handler鞭衩,它其實(shí)就是 ActivityThread 中的 mH,mH 的類型是 ActivityThread的內(nèi)部類H.在Args的run方法中有如下代碼:
final BroadcastReceiver receiver = mReceiver;
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
很顯然,這個時候 BroadcastReceiver 的 onReceiver 執(zhí)行了。Ok醋旦!