ImageVerifierCode 换一换
格式:DOCX , 页数:31 ,大小:172.08KB ,
资源ID:23097749      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/23097749.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Android 70 ActivityManagerService5 广播Broadcast相关流程分析1.docx)为本站会员(b****1)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Android 70 ActivityManagerService5 广播Broadcast相关流程分析1.docx

1、Android 70 ActivityManagerService5 广播Broadcast相关流程分析1Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析(1)一、基础知识 广播(Broadcast)是一种Android组件间的通信方式。 从本质上来看,广播信息的载体是intent。在这种通信机制下,发送intent的对象就是广播发送方,接收intent的对象就是广播接收者。 在Android中,为广播接收者定义了一个单独的组件:BroadcastReceiver。1 BroadcastReceiver的注册类型 在监听广播前,

2、要将BroadcastReceiver注册到系统中。 BroadcastReceiver总体上可以分为两种注册类型:静态注册和动态注册。静态注册 静态注册是指:通过在AndroidManifest.xml中声明receiver标签,来定义BroadcastReceiver。 PKMS初始化时,通过解析Application的AndroidManifest.xml,就能得到所有静态注册的BroadcastReceiver信息。当广播发往这种方式注册的BroadcastReceiver时,若该BroadcastReceiver对应的进程没有启动, AMS需要先启动对应的进程,然后利用Java反射机

3、制构造出BroadcastReceiver,然后才能开始后续的处理。在AndroidManifest.xml中定义BroadcastReceiver时,对应的标签类似于如下形式: 其中: android:enabled表示此broadcastReceiver是否可用,默认值为true。android:exported表示此broadcastReceiver能否接收其它App的发出的广播; 如果标签中定义了intent-filter字段,则此值默认值为true,否则为false。android:name表示BroadcastReceiver的类名。android:permission用于指定广播

4、发送方应该具有的权限; 即具有对应权限的发送者,才能通过AMS将广播发送给此broadcastReceiver;android:process表示broadcastReceiver运行的进程; BroadcastReceiver默认运行在当前app的进程中,也可以通过此字段指定其运行于其它独立的进程。intent-filter用于指定该broadcastReceiver接收广播的类型。动态注册 与静态注册不同,动态注册是指: 应用程序在运行过程中,调用Context的registerReceiver函数注册BroadcastReceiver; 当应用程序不再需要监听广播时,则需要调用unreg

5、isterReceiver函数进行反注册。动态注册BroadcastReceiver时,需要指定对应的IntentFilter。 IntentFilter用于描述该BroadcastReceiver期待接受的广播类型。 同时,动态注册时也可以指定该BroadcastReceiver要求的权限。2 广播的种类从广播的发送特点来看,可以将Android中定义的广播分为以下几类:普通广播 普通广播由发送方调用sendBroadcast及相关重载函数发送。AMS转发这种类型的广播时,根据BroadcastReceiver的注册方式,进行不同的处理流程。 对于动态注册的广播,理论上可以认为,AMS将在同

6、一时刻,向所有监听此广播的BroadcastReceiver发送消息,因此整体的消息传递的效率比较高。 对于静态注册的广播,AMS将按照有序广播的方式,向BroadcastReceiver发送消息。有序广播 有序广播由发送方调用sendOrderedBroadcast及相关重载函数发送,是一种串行的广播发送方式。处理这种类型的广播时,AMS会按照优先级,将广播依次分发给BroadcastReceiver。 AMS收到上一个BroadcastReceiver处理完毕的消息后,才会将广播发送给下一个BroadcastReceiver。 其中,任意一个BroadcastReceiver,都可以中止后

7、续的广播分发流程。 同时,上一个BroadcastReceiver可以将额外的信息添加到广播中。前文已经指出,当普通广播发送给静态注册的BroadcastReceiver时,AMS实际上是按照有序广播的方式来进行发送,这么做的原因是:静态注册的BroadcastReceiver仅申明在AndroidManifest.xml中,因此其所在的进程可能并没有启动。当AMS向这些BroadcastReceiver发送消息时,可能必须先启动对应的进程。 如果同时向静态注册的BroadcastReceiver发送广播,那么可能需要在一段时间内同时创建出大量的进程, 这将对系统造成极大的负担。若以有序广播的

8、方式来发送,那么系统可以依次创建进程, 同时,每次收到上一个广播处理完毕的消息后,都可以尝试清理掉无用的进程。 这样即可以避免突发创建大量的进程,又可以及时回收一些系统资源。因此从Android的设计方式可以看出,从接收广播的效率来讲: 排在第一的是,接收普通广播的动态注册的BroadcastReceiver, 其次是,接收有序广播的动态注册的BroadcastReceiver; 最后是,静态注册的BroadcastReceiver,此时接收的广播是否有序,已经不重要了。 实际上,源码就是按这种顺序来处理的,我们后面将进行深入分析。粘性广播 粘性广播由发送方调用sendStickyBroadc

9、ast及相关重载函数发送。 需要注意的是,目前这种广播已经被附上Deprecated标签了,不再建议使用。Android设计这种广播的初衷是: 正常情况下,假设发送方发送了一个普通广播A。 在广播A发送完毕后,系统中新注册了一个BroadcastReceiver,此时这个BroadcastReceiver是无法收到广播A的。但是,如果发送方发送的是一个粘性广播B,那么系统将负责存储该粘性广播。 于是,即使BroadcastReceiver在广播B发送完后才注册到系统,这个BroadcastReceiver也会立即收到AMS转发的广播B。粘性广播和有序广播等概念实际上不是冲突的。 粘性仅仅强调系

10、统将会保存这个广播,它的其它处理过程与上文一致。3 适时地限制广播发送范围 从上文可知,对于定义了intent-filter的BroadcastReceiver而言,其exported属性默认为true。 这种设计是符合预期的,毕竟我们使用广播的目的,就是使得属于不同应用、不同进程的组件能够彼此通信。但在某些场景下,这种设计也可能带来一些安全隐患: 1.其它App可能会针对性的发出与当前App intent-filter相匹配的广播,导致当前App不断接收到广播并处理; 2.其它App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。因此,在必要的时候,需要适

11、时地限制广播发送范围,例如: 1.对于同一App内部发送和接收广播,将exported属性设置成false; 2.在广播发送和接收时,都增加上相应的permission; 3.发送广播时,指定BroadcastReceiver对应的包名。通过以上三种方式,可以缩小广播的发送和接收范围,提高效率和安全性。实际上,在framework/support/v4/java/android/support/v4/content目录下,Android定义了LocalBroadcastManager类,用于发送和接收仅在同一个App应用内传递的广播。 这个类仅针对动态注册的BroadcastReceiver有

12、效,具体的使用方式与普通的全局广播类似, 只是注册/反注册BroadcastReceiver和发送广播时,将Context变成了LocalBroadcastManager。举例如下:/registerReceiverlocalBroadcastManager = LocalBroadcastManager.getInstance(this);localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);. /unregisterReceiverlocalBroadcastManager.unregisterRe

13、ceiver(mBroadcastReceiver);./send broadcast Intent intent = new Intent();intent.setAction(Test);localBroadcastManager.sendBroadcast(intent);.以上是Android中关于广播的基础知识,接下来进入到实际的源码分析。二、registerReceiver流程分析 现在我们看一下广播相关的源码,先从BroadcastReceiver的动态注册流程开始分析。1 ContextImpl中的registerReceiver 动态注册BroadcastReceiver,将

14、使用Context中定义的registerReceiver函数,具体的实现定义于ContextImpl中:/这是最常用的接口public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) return registerReceiver(receiver, filter, null, null);/broadcastPermission与静态注册中的permission标签对应,用于对广播发送方的权限进行限制/只有拥有对应权限的发送方,发送的广播才能被此receiver接收/不指定scheduler时

15、,receiver收到广播后,将在主线程调用onReceive函数/指定scheduler后,onReceive函数由scheduler对应线程处理public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) /ContextImpl是Context家族中实际工作的对象,getOuterContext得到的是ContextImpl对外的代理 /一般为Application、Activity、Service

16、等 return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext();与上述代码类似,不论调用Context中的哪个接口注册BroadcastReceiver,最终流程均会进入到registerReceiverInternal函数。现在,我们跟进一下registerReceiverInternal函数:private Intent registerReceiverInternal(BroadcastReceiver receiver,

17、int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) IIntentReceiver rd = null; if (receiver != null) /mPackageInfo的类型为LoadedApk if (mPackageInfo != null & context != null) if (scheduler = null) /未设置scheduler时,将使用主线程的handler处理 /这就是默认情况下,Receiver的onReceive函

18、数在主线程被调用的原因 scheduler = mMainThread.getHandler(); /getReceiverDispatcher函数的内部,利用BroadcastReceiver构造出ReceiverDispatcher /返回ReceiverDispatcher中的IIntentReceiver对象 /IIntentReceiver是注册到AMS中,供AMS回调的Binder通信接口 rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumenta

19、tion(), true); else /这段代码写的我瞬间懵逼了。scheduler = null对应的判断,明显可以移出if-else结构的 /不符合大google的逼格 if (scheduler = null) scheduler = mMainThread.getHandler(); /主动创建 rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver(); try /将Receiver注册到AMS final Intent intent =

20、ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId); . catch (RemoteException e) . 以上代码中主要的工作是: 创建BroadcastReceiver对应的ReceiverDispatcher,得到ReceiverDispatcher内部的IIntentReceiver对象, 然后利用Binder通信将该对象连同Broadca

21、stReceiver的其它信息注册到AMS中。IIntentReceiver对应的继承关系如下图所示:结合上图,我们需要知道的是: 1、BroadcastReceiver收到的广播实际上是AMS发送的,因此BroadcastReceiver与AMS之间必须进行Binder通信。 从类图可以看出,BroadcastReceiver及其内部成员并没有直接继承Binder类。负责与AMS通信的,实际上是定义于LoadedApk中的InnerReceiver类,该类继承IIntentReceiver.Stub,作为Binder通信的服务端。 从上文的代码可以看出,注册BroadcastReceiver

22、时,会将该对象注册到AMS中供其回调。当InnerReceiver收到AMS的通知后,将会调用ReceiverDispatcher进行处理。 由于ReceiverDispatcher持有了实际的BroadcastReceiver,于是最终将广播递交给BroadcastReceiver的onReceive函数进行处理。以上的设计思路实际上是: 将业务接口(处理广播的BroadcastReceiver类)与通信接口(InnerReceiver类)分离,由统一的管理类(ReceiverDispatcher)进行衔接。 这基本是Java层使用Binder通信的标配,AIDL本身也是这种思路。2、Bro

23、adcastRecevier中定义了一个PendingResult类,该类用于异步处理广播消息。从上文的代码我们知道了,如果没有明确指定BroadcastReceiver对应的handler,那么其onReceive函数将在主线程中被调用。 因此当onReceive中需要执行较耗时的操作时,会阻塞主线程,影响用户体验。 为了解决这种问题,可以在onReceive函数中采用异步的方式处理广播消息。一提到异步的方式处理广播消息,大家可能会想到在BroadcastReceiver的onReceive中单独创建线程来处理广播消息。然而,这样做存在一些问题。例如: 对于一个静态注册的BroadcastR

24、eceiver 对象,对应的进程初始时可能并没有启动。 当AMS向这个BroadcastReceiver对象发送广播时,才会先启动对应的进程。 一旦BroadcastReceiver对象处理完广播,i并将返回结果通知给AMS后,AMS就有可能清理掉对应进程。 因此,若在onReceive中创建线程处理问题,那么onReceive函数就可能在线程完成工作前返回,导致AMS提前销毁进程。 此时,线程也会消亡,使得工作并没有有效完成。为了解决这个问题,就可以用到BroadcastReceiver提供的PendingResult。 具体的做法是: 先调用BroadcastReceiver的goAsyn

25、c函数得到一个PendingResult对象, 然后将该对象放到工作线程中去释放。 这样onReceive函数就可以立即返回而不至于阻塞主线程。 同时,Android系统将保证BroadcastReceiver对应进程的生命周期, 直到工作线程处理完广播消息后,调用PendingResult的finish函数为止。其中原理是: 正常情况下,BroadcastReceiver在onReceive函数结束后, 判断PendingResult不为null,才会将处理完毕的信息通知给AMS。一旦调用BroadcastReceiver的goAsync函数,就会将BroadcastReceiver中的Pe

26、ndingResult置为null, 因此即使onReceive函数返回,也不会将信息通知给AMS。 AMS也就不会处理BroadcastReceiver对应的进程。待工作线程调用PendingResult的finish函数时,才会将处理完毕的信息通知给AMS。代码示例如下:private class MyBroadcastReceiver extends BroadcastReceiver . public void onReceive(final Context context, final Intent intent) /得到PendingResult final PendingResu

27、lt result = goAsync(); /放到异步线程中执行 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 sHandl

28、er; static sHandlerThread.start(); sHandler = new Handler(sHandlerThread.getLooper(); public static void post(Runnable r) sHandler.post(r); private AsyncHandler() 需要注意的是: 在onReceive函数中执行异步操作,主要目的是避免一些操作阻塞了主线程, 但整个操作仍然需要保证在10s内返回结果,尤其是处理有序广播和静态广播时。 毕竟AMS必须要收到返回结果后,才能向下一个BroadcastReceiver发送广播。2 AMS中的r

29、egisterReceiver AMS的registerReceiver函数被调用时,将会保存BroadcastReceiver对应的Binder通信端,我们一起来看看这部分的代码:public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) . /保存系统内已有的sticky广播 ArrayList stickyIntents = null; . synchronized(this) if (caller != null) /根据IApplicationThread得到对应的进程信息 callerApp = getRecordForAppLocked(caller); if (callerApp = null) /抛出异常,即不允许未登记的进程注册BroadcastReceiver . if (callerApp.info.uid != Proc

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1