动态注册广播接收器_ip广播系统软件v2.3说明书
大家好,又见面了,我是你们的朋友全栈君。
从registerReceiver(BroadcastReceiver receiver,IntentFilter filter)出发
所经历的类和方法:registerReceiver(receiver,filter)–>ContextWrapper.java$registerReceiver(receiver,filter);
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);//Context mBase
}
Context是接口,其实现类ContextImpl,因此其具体实现是ContextImpl$reisterReceiver(receiver,filter);
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
int flags) {
return registerReceiver(receiver, filter, null, null, flags);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
用以上代码可知调用registerReceiver以后其内部调用了其重载函数,其重载函数又进一步调了registerReceiverInternal()方法
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler(); //1
}
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 = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
IIntentReceiver :IIntentReceiver其实是一个AIDL接口,内部就一个方法performReceive(…); mPackageInfo:ContextImpl导入的一个LoadedApk的实例,是用来处理广播接收的。
以上参数context通过getOuterContext获取注册广播时所在的context,方法走到//1处,scheduler = mMainThread.getHandler();该scheduler是一个Handler类型,mMainThread是ActivityThread类型,这里handler主要用来负责分发AMS发送过来的广播用的。
public final class ActivityThread {
......
final H mH = new H();
private final class H extends Handler {
......
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
}
......
}
.....
}
......
final Handler getHandler() {
return mH;
}
获取完scheduler(Handler),执行 rd = mPackageInfo.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;
return rd.getIIntentReceiver();
}
}
首先这个函数的返回类型是IIntentReceiver,这里可以看做是帮我们跨进程通信的Binder对象。
mReceivers:private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers是通过这些数组和Map把注册广播所在的Context,receiver以及ReceiverDispatcher封装起来,逻辑是如果广播registered成立,以context为key获取broadcastReceiver和ReceiverDispatcher。
假设MainActivity为Key值保存在LoadedApk的成员变量mReceivers中,这样,只要给定一个Activity和BroadcastReceiver,就可以查看LoadedApk里面是否已经存在相应的广播接收发布器ReceiverDispatcher了。
看看ReceiverDispather这个类
static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
InnerReceiver是静态内部类,继承IIntentReceiver.Stub,InnerReceiver就是Binder接口IIntentReceiver的实现类
return rd.getIIntentReceiver();-->return mIIntentReceiver
mIIntentReceiver = new InnerReceiver(this, !registered);
而通过以上rd.getIIntetReceiver可以看出rd就是InnerReceiver对象,也就是binder对象
在新建广播接收发布器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,可以通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的参数activityThread保存下来,以便后面在分发广播的时候使用。
接下来是 Intent intent = ActivityManager.getService().registerReceiver()
.....
Intent intent = ActivityManager.getService().registerReceiver()
...
最后进入到ActivityManagerService中的registerReceiver函数中去,ActivityMangerService$registerReceiver();看看它是怎么进去的
过往的版本貌似都是ActivityManagerNative.getDefault().registerReceiver()而不是ActivityManager.getService().registerReceiver(),看看两者的实现步骤都具体做了什么,有什么区别
<1> 我先定位到ActivityManger.getService()这个方法
/**
* @hide
*/
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
接下来看看IActivityMangerSingletone()
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
在frameworks/base/core/java/android/util/Singleton.java
public abstract class Singleton<T> {
@UnsupportedAppUsage
private T mInstance;
protected abstract T create();
@UnsupportedAppUsage
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
单纯从代码分析以上两段代码,IActivityManagerSingleton是Singleton变量,而singleton是一个抽象类内部声明了一个抽象方法create(),而其get方法通过由一个继承了Singleton并复写函数create()来给mInstance赋值,回到上层IActivityManagerSingleton,new了Singleton并重写了函数create(),通过Context.ACTIVITY_SERVICE获取一个binder,,并通过该对象返回了IActivityManager对象,最后ActivityManager.getService().registerReceiver()等价于IActivityManager.registerReceiver()。
具体以上的代码,就是Singleton这么一个工具类通过单例模式创建了一个对象,而这个过程是这样的,先获取了关联了系统服务的ActivityManagerService的binder对象[ final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);],然后通过IActivityManager.Stub.asInterface(b)返回一个IActivityManager的代理对象,基于Binder机制,通过调用代理对象的方法,使得系统服务ActivityManagerService对应的方法被调用,因为ActivityManagerService继承了IActivityManager.Stub
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
}
<2>ActivityManagerNative.getDefault().registerReceiver()看看他是怎么个操作
static public IActivityManager getDefault(){
return ActivityManager.getService();
}
我丢!一样啊,只是这个方法封装了一下而已,到头来还是ActivityManager.getService(),打扰了–。
ActivityManagerService.registerReceiver的处理
函数首先是获得调用registerReceiver函数的应用程序进程记录块:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver,
IntentFilter filter,String permission, int userId,int flags) {
... //初始化了一些需要的变量
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
}
if(cllerApp == null){
....//异常处理
}if (callerApp.info.uid != SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
...//抛异常
}
callingUid = callerApp.info.uid;//进程中第一个app的uid
callingPid = callerApp.pid;该app应用的process id
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
instantApp = isInstantApp(callerApp, callerPackage, callingUid);
看看instantApp是什么,怎么获取的
private boolean isInstantApp(ProcessRecord record, @Nullable String callerPackage, int uid) {
if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
return false;
}
// Easy case -- we have the app's ProcessRecord.
if (record != null) {
return record.info.isInstantApp();
}
// Otherwise check with PackageManager.
IPackageManager pm = AppGlobals.getPackageManager();
try {
if (callerPackage == null) {
final String[] packageNames = pm.getPackagesForUid(uid);
if (packageNames == null || packageNames.length == 0) {
throw new IllegalArgumentException("Unable to determine caller package name");
}
// Instant Apps can't use shared uids, so its safe to only check the first package.
callerPackage = packageNames[0];
}
mAppOpsService.checkPackage(uid, callerPackage);
return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid));
} catch (RemoteException e) {
Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e);
return true;
}
}
在Process.java中FIRST_APPLICATION_UID=10000;规定应用Uid是10000-19999,因此if(UserHandle.getAppId(uid) < FIRST_APPLICATION_UID)这就是判断如果不是应用的uid就返回false.
然后通过PackageManager检查是不是instant app如果是返回ture;
以下是处理粘性广播的处理
有时候用户希望发送sticky广播,以便日后注册的receiver可以收到“错过”的sticky广播。要达到这个目的,系统必须在内部维护一张sticky广播表,在具体的实现中,AMS会把广播intent加入mStickyBroadcasts映射表中。mStickyBroadcasts是一张哈希映射表,其key值为intent的action字符串,value值为“与这个action对应的intent数组列表”的引用。当我们发送sticky广播时,新的广播intent要么替换掉intent数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。
Iterator<String> actions = filter.actionsIterator();
...
int[] userIds = { UserHandle.USER_ALL,UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {//mStickyBroadcasts保留着系统所有的粘性广播
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
stickyIntents.addAll(intents);
}
}
//到此为止stickyIntents是系统中所有的粘性的intent
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
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);
// If intent has scheme "content", it will need to acccess
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
//allSticky就是与本次注册intentfilter匹配的粘性广播
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i); //处理粘性广播
BroadcastQueue queue = broadcastQueueForIntent(intent);
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);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
1.mStickyBroadcasts,key是用户的id,data是广播intent数组。由此可见,粘性广播是分用户的,用户1发出的粘性广播,用户2可能收不到。
2.stickyIntents 保存的是系统所有粘性广播intent
3.allSticky 保存的是与本次registerReceiver(BroadcastReceiver receiver, IntentFilter intent)中intent匹配的粘性广播的intent
扩展:粘性消息:粘性消息在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据. 注意:普通广播和粘性消息不同被截获,而有序广播是可
如果不是用sendStickyBroadcat()发的 因此这里的sticky和allSticky都为null 会进入以下代码
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
final int totalReceiversForApp = rl.app.receivers.size();
if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
throw new IllegalStateException("Too many receivers, total of "
+ totalReceiversForApp + ", registered for pid: "
+ rl.pid + ", callerPackage: " + callerPackage);
}
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);
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
其中的receiver参数类型是IIntentReceiver,也就是对应着之前的ReceiverDispatcher中那个binder实体。
下面是ReceiverList的代码片段:
class ReceiverList extends ArrayList<BroadcastFilter>implements IBinder.DeathRecipient {
final ActivityManagerService owner;
public final IIntentReceiver receiver;
public final ProcessRecord app;
public final int pid;
public final int uid;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
. . . . . .
ReceiverList继承于ArrayList,而BroadcastFilter又继承于IntentFilter,所以ReceiverList可以被理解为一个IntentFilter数组列表。
就是把注册的receivers保存到这个ReceiverList表里,其中还判断了如果注册的receiver数量过多抛出异常,最后把这receivers表放在mRegisteredReceivers中,这些都是为了方便在收到广播时,快速找到对应的广播接收器的。其中rl.app可以看做是这个注册receiver的宿主app的进程。
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
if (rl.containsFilter(filter)) {
...
mReceiverResolver.addFilter(bf);
}
以上代码作用就是创建一个BroadcastFilter来把广播接收器列表rl和filter关联起来,然后保存在ActivityManagerService中的成员变量mReceiverResolver中去,因为之前只保存receivers没有和其filter发生关联
BroadCastFilter代码片段:
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
String _packageName, String _requiredPermission, int _owningUid, int _userId,
boolean _instantApp, boolean _visibleToInstantApp) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
owningUid = _owningUid;
owningUserId = _userId;
instantApp = _instantApp;
visibleToInstantApp = _visibleToInstantApp;
}
...
最后将BroadcastFilter添加到IntentResolver类型的mReceiverResolver中,这样当AMS接收到广播时,就可以从mReceiverResolver中直接找到对应的广播接收者,从而达到注册广播的目的。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/194686.html原文链接:https://javaforall.cn
相关文章
- 万字长文 | Spring Cloud Alibaba组件之Nacos实战及其客户端服务注册源码解析
- LiveGBS流媒体平台GB/T28181常见问题-海康大华宇视华为NVR摄像头无法注册到国标平台看不到设备的时候如何抓包及排查
- IP数据云教你网站ip地址查询的方法
- 【零开始搭建SpringCloud Alibaba】搭建配置中心及注册中心nacos(Server端)
- 注册邮箱发送短信验证_网易邮箱注册系统未收到短信
- 第123期:用vue3结合hooks开发一个可以注册的二次确认弹框
- 记一次dubbo连接zookeeper注册中心发生异常的排查经历
- DoubleTake for Mac(全景图制作软件) v2.6.10注册激活版
- fl studio21版本如何更新,2023年怎么永久激活fl studio免费升级注册解锁
- 申请WERCS注册需要提供符合最新标准的MSDS/SDS 化学品安全技术说明书
- 【EventBus】事件通信框架 ( 订阅方法注册 | 注册 事件类型 - 订阅类 + 订阅方法 到指定集合 | 取消注册 数据准备 )
- 总结了ChatGPT-4的国内使用方法以及注册教程
- @EnableEurekaClient 无法引入解决方案【SpringCould-eureka 注册服务】
- Consul 的服务注册和发现原理(一)
- 分配Linux下的IP分配机制(linux允许ip)
- Linux:统计IP的奇妙之旅(linux统计ip)
- MySQL远程连接:通过IP访问数据库(mysql通过ip访问)
- Linux下快捷注册用户的方式(linux注册用户)
- Linux:实现动态IP变化(linux动态ip)
- 微信公众号和个人微信账号均已恢复注册
- 掌握 Linux 服务注册的方法及重要性(linux服务注册)
- Linux系统中如何设置多网卡多IP?(linux多网卡多ip)
- 解决无法使用IP访问MSSQL的方法(无法用ip访问mssql)
- Linux下IP连接优化提升服务效率(linux ip连接数)
- MySQL用户IP记录功能详解(mysql用户ip)
- Oracle注册监听:解锁数据库无束缚之力(oracle注册监听)
- 管理Oracle注册监听管理指南(oracle注册监听)
- Linux用户IP记录:有效管理访问你的系统(linux用户ip)
- 注册Redis缓存遭遇失败一场灾难(注册redis缓存失败)
- 新建Redis服务器无需IP就可使用(新建redis 没有ip)
- 解决mysql无法通过IP连接的问题(mysql不能用ip链接)
- 安全限制使用Redis禁止IP访问(redis限定ip访问)
- 轻松搞定Oracle网络IP配置(oracle ip配置)
- Redis访问权限控制IP受限(redis 访问ip受限)
- 一个简易需要注册的留言版程序
- ASP.NET中使用后端代码注册脚本生成JQUERY-EASYUI的界面错位的解决方法
- ASP+ajax注册即时提示程序代码
- PHP+jQuery注册模块的改进(二):邮箱激活