Android广播机制剖析【android广播系列一】
广播是android四大组件之一,所以广播的重要性也是显而易见的,最近需要做个功能,需要对广播的机制深入了解,所以我就整理下思路。平时我们写代码的时候,广播只是用来通知机制的,不是用来通信机制,通信机制还是要用binder机制来实现。但是现在市场上的第三方app大部分都是滥用广播,用广播来监听一些事件来实现自启动或者后台启动。这就误解了谷歌的意思。本来很好的组件,被大家滥用,导致用户老是感觉手机的程序控制不住,老是后台偷偷启动。小白用户甚至更不理解了。好了现在言归正传。
android广播按发送方式分为三种:
- 无序广播
- 有序广播 :OrderedBroadcast
- 粘连广播 :StickyBroadcast
广播按照注册方式分为两种:
- 静态广播
动态广播
摘录老罗文章“广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,而广播接收者需要先订阅消息,然后才能接收到消息。在广播机制中,广播发送者事先不需要知道广播接收者的存在的,这样就能大大降低广播发送者和广播接收者之间的耦合度,进而提高系统的可扩展性和可维护性。”
广播的正真机制就两种情况,注册广播的机制,和接收广播的机制,我们先来谈谈注册广播的流程。
注册广播的流程:分为动态注册和静态注册两种;
(1)先来说说动态注册广播
Step1:说明: ReceiverDispatcher是frameworks/base/core/java/android/app/LoadedApk.java的内部类,这个类是保存广播接收者的组件(Activity或者Service),负责将这个被注册的广播接收者和注册它的组件关联在一起。
static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
. . . . . .
. . . . . .
}
// 请注意这个域!它就是传到外面的rd。
final IIntentReceiver.Stub mIIntentReceiver;
final BroadcastReceiver mReceiver;
final Context mContext;
final Handler mActivityThread;
final Instrumentation mInstrumentation;
final boolean mRegistered;
final IntentReceiverLeaked mLocation;
RuntimeException mUnregisterLocation;
boolean mForgotten;
. . . . . .
Step2:好了先说说需要了解的知识。registerReceiver(),是通过context来注册的,调用过程最后调用到ContextImpl这个对象中,
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, filter, broadcastPermission, scheduler, getOuterContext());
}
Step3:我们接着来看看registerReceiverInternal()这个方法:
private Intent registerReceiverInternal(BroadcastReceiver
receiver, 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();
}
// 查找和context对应的“子哈希表”里的ReceiverDispatcher,
//如果找不到,就重新new一个
rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(..., ...);
}
. . . . . .
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission);
} catch (RemoteException e) {
return null;
}
}
Step4:大家注意那个rd对象(IIntentReceiver rd),首先调用成员变量mPackageInfo的成员函数(是LoadedApk的一个对象)getReceiverDispatcher()将广播接收者receiver封装成一个实现了IIntentReceiver接口的Binder的本地对象rd,最后将这个对象发送给AcitivityManagerService。
接着来看看getReceiverDispatcher()这个方法:
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;
//来获得一个实现了IIntentReceiver接口的Binder的本地对象
return rd.getIIntentReceiver();
}
}
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
Step5:接着来看看给mIIntentReceiver赋值的地方,
static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
。。。 。。。
}
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
//指向了一个实现IIntentReceiver接口的Binder的本地对象
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
}
Step6:注意:这个InnerReceiver对象内部有一个类型为ReceiverDispatcher的弱引用mDispatcher,它指向一个外部的ReceiverDispatcher对象。
接着就注册到AMS中去了。代码如下:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid;
int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {//得到调用者的ProcessRecord
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = this.handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
List allSticky = null;
// Look for any matching sticky broadcasts...
Iterator actions = filter.actionsIterator();
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.getUserId(callingUid));
}
} else {
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.getUserId(callingUid));
}
// The first sticky in the list is returned directly back to
// the client.//判断黏性广播是否为null
Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
+ ": " + sticky);
if (receiver == null) {
return sticky;
}
ReceiverList rl //这段代码注意看,很有研究价值。
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
throw new IllegalArgumentException(
"Receiver requested to register for uid " + callingUid
+ " was previously registered for uid " + rl.uid);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
好了今天就到这里吧,后续再整理。。。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。