BroadcastReceiver探讨之动态广播注册流程
来源:互联网 发布:域名注册量查询 编辑:程序博客网 时间:2024/05/17 07:26
BroadcastReceiver
博文来源于
BroadcastReceiver探讨之动态广播注册流程
csdn:linyh1992
源码:
frameworks/base/+/android-4.4.1_r1/core/java/android/app/ContextImpl.java
frameworks/base/+/android-4.4.1_r1/core/java/android/app/ActivityManagerNative.java
frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/ActivityManagerService.java
frameworks/base/+/android-4.4.1_r1/core/java/android/app/LoadedApk.java
frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/ReceiverList.java
frameworks/base/+/android-4.4.1_r1/services/java/com/android/server/am/BroadcastFilter.java
相关
在Android中,BroadcastReceiver是一套用来实现组件之间的通信机制,它是基于消息发布和订阅机制,可以用在不同线程之间组件的通信,也可以跨进程进行组件间通信。
在Android中,根据注册类型来看,BroadcastReceiver可以分为两种类型:静态广播和动态广播,其中静态广播中的广播中心是PMS(PackageManagerService),而动态广播的广播中心是AMS(ActivityManagerService),这边文章主要是分析动态广播的。
动态BroadcastReceiver的注册是借助Context接口中的registerReceiver方法来实现的。其中,继承Context接口的类有:Activity,Service,Application,所以在这3个类中,都可以动态注册广播。此外,Context接口的实现类,是ContextImpl类。
在讨论BroadcastReceiver前,需要稍微了解下Android不同进程之间的通信,以及一些Android系统级的Service。
Android跨进程之间的通信,主要有Binder实现的IPC机制,匿名共享内存,socket等等,而广播的跨进程通信是使用Binder来实现的,通过Binder将App进程切换到system_server进程和AMS通信。
在手机开机的时候,Android启动了很多系统级的进程,比如zygote进程,system_server进程,其中在system_server进程上,又启动了很多服务,
比如PowerManagerService(电源管理服务),ActivityManagerService(管理四大组件)
下面,主要是通过探讨广播,主要是分为两部分,
用户进程和system_server进程,广播registerReceiver的流程
用户进程和system_server进程,广播底层的数据存储结构
广播在用户进程中的注册流程
Context.java
public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter);
receiver: 广播接收组件,接收到相关的广播会调用BroadcastReceiver中的onReceive方法
filter:主要是用来过滤广播的,只有符合条件的广播才会接收.
registerReceiver方法最终是调用ContextImpl中的registerReceiverInternal方法
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 { return ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId); } catch (RemoteException e) { return null; }}
mPackageInfo:LoadedApk类型,保存了整个App内部的信息,比如包名,资源的路径,应用中所有启动的service,注册的reciver等等,用户进程(即一个应用进程)中,只会存在一个对象,所有ContextImpl中的mPackageInfo都指向共同的mPackageInfo对象,因此,mPackageInfo在整个用户进程中是唯一的。
registerReceiverInternal方法:
1,获取Handler类型的scheduler对象,如果scheduler不为null,则scheduler为用户传递进来的Handler,否则为主线程中的Hanlder(这也是为什么BroadcastReceiver中的onReceive方法调用是处于主线程的原因),这个Handler对象主要是用来分发AMS传递过来的Intent,
2,如果mPackageInfo存在,则通过mPackageInfo获取IIntentReceiver类型的rd对象,否则创建一个,rd同样也是Binder类型。然后,将rd传递给AMS,AMS处理相对应的广播,会通过这个Binder对象,跨进程回传到这个Context所处的进程中。
3,通过ActivityManagerNative.getDefault()获取到ActivityManagerProxy对象,然后借助ActivityManagerProxy对象,和AMS通信,这时候进程由用户进程切换到system_server进程,最终调用的是AMS中的registerReceiver方法。
scheduler = mMainThread.getHandler();
mMainThread是ActivityThread对象,也可以说是主线程
这里有个疑惑,关于mPackageInfo,这个对象什么时候不存在?(以后再查看)按照我目前的理解,mPackageInfo最早是在Application创建的时候,就被初始化了,而context是当前的Activity,Service或者Application,所以符合条件(mPackageInfo != null && context != null)
rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler, mMainThread.getInstrumentation(), true);
LoadedApk
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(); }}
1,如果广播已经注册过,则通过mReceivers对象获取key为当前Context的LoadedApk.ReceiverDispatcher类型的rd对象
2,如果rd对象不存在,则创建,并且将rd添加到map中;否则,验证rd对象中的context和activitythread(validate这个方法有疑惑,什么场景下,rd中context和activitythread和目前的不一致)
3,返回rd对象的中IIntentReceiver对象
广播在用户进程中的底层数据存储
上面说过,在用户进程中所有ContextImpl中的mPackageInfo都指向同一对象。
mPackageInfo中存在维持一个ArrayMap的mReceivers变量,mReceivers是ArrayMap嵌套ArrayMap的数据结构
key:为注册的Context(即Activity,Service或者Application),
value:同样也是个ArrayMap,
key:注册的BroadcastReceiver
value:ReceiverDispatcher对象,广播分发者,该对象内部保存注册者的Context,Handler,BroadcastReceiver,IIntentReceiver.Stub(和用户进程交互的)等等,
发送广播时,AMS将会将Intent信息分发到BroadcastReceiver的onRecieve方法中。
mReceivers
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
LoadedApk.ReceiverDispatcher
final IIntentReceiver.Stub mIIntentReceiver;final BroadcastReceiver mReceiver;final Context mContext;final Handler mActivityThread;final Instrumentation mInstrumentation;final boolean mRegistered;final IntentReceiverLeaked mLocation;RuntimeException mUnregisterLocation;//....
广播在AMS中的注册流程
try { // 跨进程和AMS通信,广播的注册实际上是在AMS中执行的 return ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId);} catch (RemoteException e) { return null;}
ActivityManagerNative.getDefault()
public abstract class ActivityManagerNative extends Binder implements IActivityManager{ // ...省略 // 获取到唯一的 static public IActivityManager getDefault() { return gDefault.get(); } // ...省略 // Singleton是一个抽象类,Android实现的单例模式 private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { // 获取指向ActivityManagerService的IBinder对象 IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " + b); } // 创建ActivityManagerProxy对象,并将ActivityManagerService的Binder传递给ActivityManagerProxy IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " + am); } return am; } }; // 创建一个ActivityManagerProxy对象 static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ActivityManagerProxy(obj); }}
ActivityManagerNative.getDefault()第一次会创建一个AMP(ActivityManagerProxy)对象,并通过ServiceManager远程获取到一个指向AMS
的IBinder对象,并将这个IBinder引用传递给AMS对象,最后返回AMP对象,注意这个对象运行在用户进程中的,
ActivityManagerProxy中的IBinder引用是用来跨进程和AMS通信的。
ActivityManagerProxy.registerReceiver
public Intent registerReceiver(IApplicationThread caller, String packageName, IIntentReceiver receiver, IntentFilter filter, String perm, int userId) throws RemoteException{ Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeString(packageName); data.writeStrongBinder(receiver != null ? receiver.asBinder() : null); filter.writeToParcel(data, 0); data.writeString(perm); data.writeInt(userId); // 跨进程和AMS通信,注意Binder驱动会挂起当前用户线程 mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0); reply.readException(); Intent intent = null; int haveIntent = reply.readInt(); if (haveIntent != 0) { intent = Intent.CREATOR.createFromParcel(reply); } reply.recycle(); data.recycle(); return intent;}
很明显,这段代码是用来跨进程通信的。mRemote是一个IBinder对象,指向AMS的。
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
Binder机制非常复杂,这里不做讨论。我们只需要知道IBinder.transact方法,会调用到IBinder.onTransact方法。
题外话:
这里有个疑惑,mRemote.transact会不会阻塞线程呢?仔细思考了下,应该会的,准确的来说,Binder驱动会挂起当前用户进程。
因为registerReceiver返回Intent对象,如果当前用户线程没有挂起的话,那么后面的代码就没意义了,比如reply.readException肯定没效果。
mRemote这个IBinder对象是指向AMS的,而AMS继承了ActivityManagerNative类,并且AMS的onTransact方法是由ActivityManagerNative实现的。
ActivityManagerNative.onTransact,注意调用这个方法的时候,进程已经从用户进程切换到system_server进程了。
@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case REGISTER_RECEIVER_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null; String packageName = data.readString(); b = data.readStrongBinder(); IIntentReceiver rec = b != null ? IIntentReceiver.Stub.asInterface(b) : null; IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data); String perm = data.readString(); int userId = data.readInt(); // 调用AMS中的registerReceiver方法,这个方法是广播注册的最终实现 Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId); reply.writeNoException(); if (intent != null) { reply.writeInt(1); intent.writeToParcel(reply, 0); } else { reply.writeInt(0); } return true; } }
ActivityManagerService.registerReceiver
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) { 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, true, "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. 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; }}
1,首先会获取应用的uid和pid,然后调用handleIncomingUser检测注册广播所需的权限,并获取userId
2,检测所有符合条件的黏性事件,如果广播为null,则返回最近的一条黏性事件
3,通过mRegisteredReceivers获取key为receiver(IBinder对象,从用户进程中传递进来的IIntentReceiver对象),获取相关的ReceiverList;
如果ReceiverList为null,则创建,并添加到mRegisteredReceivers中4,创建BroadcastFilter(继承IntentFilter)对象,并添加进入到ReceiverList中
5,处理符合条件的黏性事件(这里暂不探讨)
广播在AMS中的底层数据存储
ReceiverList:继承ArrayList,存储的是BroadcastFilter对象
BroadcastFilter:继承IntentFilter,主要是用来过滤广播的,广播接收器只能接收指定的广播类型
AMS中维持一个mRegisteredReceivers变量的HashMap
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<IBinder, ReceiverList>();
mRegisteredReceivers的数据结构是HashMap
key:IBinder,也就是ReceiverDispatcher中的IIntentReceiver对象,是用于AMS和用户进程通信的
value:ReceiverList,存储一系列的BroadcastFilter的List
mReceiverResolver:存储对应的BroadcastFilter
动态广播注册的底层数据存储
参考:
Android应用程序注册广播接收器(registerReceiver)的过程分析
四大组件的工作过程
Android中Context详解 —- 你所不知道的Context
品茗论道说广播(Broadcast内部机制讲解)(上)
Android 内核–Context对象
- BroadcastReceiver探讨之动态广播注册流程
- BroadcastReceiver探讨之动态广播取消注册流程
- BroadcastReceiver静态注册广播与动态注册广播接收器
- BroadcastReceiver register 广播的动态注册方式
- 注册广播BroadcastReceiver
- BroadcastReceiver之动态广播 demo+笔记
- BroadcastReceiver广播静态注册细节
- Android 广播BroadcastReceiver静态/动态注册,获取系统电量广播【开机广播】,获取网络状态广播,Timer的使用简介
- BroadcastReceiver广播接听器的初步认识,动态注册,静态注册(1)
- BroadCastReceiver 静态动态注册
- BroadCastReceiver 静态动态注册
- Android:动态注册BroadcastReceiver
- BroadcastReceiver 动态注册
- BroadcastReceiver组件 动态注册
- Android广播之注册广播(包括静态广播和动态广播的注册)源码分析
- BroadcastReceiver之系统广播
- BroadcastReceiver 之有序广播
- BroadCastReceiver之本地广播
- mysql数据库分配、取消权限
- nginx(转)
- Mesos源码分析(2): Mesos Master的启动之一
- 控件收集
- Java 修饰符
- BroadcastReceiver探讨之动态广播注册流程
- 【uoj218】火车管理 题解&代码(C++)
- 动态规划之最长公共子序列和最长公共字串,最大子序列和
- iOS与网页JS交互,看我就够了
- ARMv8 Linux内核head.S源码分析
- jtable单元格添加icon图标
- 动态规划(3)格子取数问题
- 对象引用与对象的区别
- 正则表达式笔记