普通视图

Google Play 应用上架二三事

2025年7月13日 21:05

首次上架应用到google play还是2015年的时候,那个时候上架应用限制比较少,注册个账号信用卡付个款就行了。自己之前有个账号,但是闲置许久,加上自己的一些骚操作账号被Google给永久禁用了,并且还给我发了个邮件告知不要再尝试注册新的账号了。最近自己的新应用想要上架,于是又重新注册了个个人账号。同时公司的产品也要上架Google play,前前后后经过了小半年的折腾才终于搞定,将应用上架,这里就来说道说道。

账号注册

早期的时候,无论是个人账号还是公司账号注册都不需要实名验证的,因此只需要填一下联系信息使用信用卡付款25美元就可以直接注册成功了。但是从2023年开始,无论是已有账号还是新注册的账号都需要进行验证。对于个人账号,只需要填写一份付款资料,并且验证身份,身份证,护照信息都可以,这个信息需要与付款资料中所填写的需要一样,并不要求付款信用卡的账单地址和姓名与这个相同。

对于公司账号的注册,则是和苹果一样要求提供邓白氏码(D-U-N-S Number),输入邓白氏码之后会自动获取到公司的名称地址信息生成付款资料。和个人账号一样,付款的信用卡也没啥要求。在验证的时候,则是需要提供公司的政府签发的文件,比如国内的营业执照,具体可以查看Google play 官方文档。公司的验证也还需要验证一位开发发者的信息,这里的要求跟个人差不多,身份证,护照等都可以,而且不需要提供在这家公司任职的任何证明文件,公司注册地和个人不在同一个国家也没关系(这一点,苹果的开发者注册是要求提供个人在公司任职的证明文件或者名片之类的东西的)。

如果所有的这些证明文件都能够顺利的提供,并且邮箱验证和电话验证都没问题,那么账号注册是很容易的。最后有一点需要补充,付款的信用卡是不能使用银联卡的,需要visa或者mastercard。总体来说,比苹果开发者账号要容易,就上面说的我的个人账号被封之后,使用家人的信息又重新顺利注册了一个新账号。而公司的账号,在获取法务同时完成公司DUNS Number的申请之后,也都顺利的完成注册了。但这些完成之后,也还是只是完成了万里长征的第一步。

个人应用上架

以前个人应用在google play上架是很容易的,而在2023年11月,google 出了一个新政策,对于在23年11月13日之后注册的新账号,发布应用时必须满足特定的测试要求才能正式发布。具体要求是,正式发布之前,需要在google play上进行封闭测试,需要至少12名测试人员测试至少14天持续参与测试。这个对于个人开发者来说,还是不太容易达成的,这至少12名测试人员,是需要开发者将他们的Google 邮箱输入到google play开发后台,他们接受并且根据开发者提供的链接进入下载安装的。至于连续14天的持续参与测试,这个不太清楚Google 是如何统计的,封闭时间肯定是要保证14天以上,但是如何保证每天都至少有12人参与这个不确定是否强制要求。

我的应用在开发完成之后,通过在小众软件和Linux.do社区征集到了一定的热心网友参与了测试,从而顺利完成了封闭测试,对于工具类的软件这是个不错的方式。对于这一点,虽然加大了个人上架应用的难度,但是我想也是可以提高上架应用质量,毕竟对于个人开发开发者来说,测试相比于公司开发的应用来说会更加薄弱。

公司应用上架

公司开发者应用的上架,没有上面对于个人开发者的限制,但是我们在上架的时候遇到了更多的问题。因为我们所在的是金融行业,在上架的时候会更加的敏感,因此也更需要小心一点。

我们在第一次上架的时候,填完了所有的信息,上传了应用,提交审核之后,谷歌以我们需要登录为由审核不通过。提交了登录需要的信息之后,等了超过十天,结果直接账号被禁用了,原因是高风险,发邮件和申述都没用,并且不告知具体的原因。搞得我们很受伤,不知道该怎么办。商量之后决定让公司注册新的实体再重新注册开发者账号。

另一方面,想到公司以前的应用可能还有老的开发者账号,或许可以用,找回了老账号的邮箱,使用新的公司主体信息进行了验证,之后顺利的提交了应用。这一次为了稳妥起见,我们先提交了封闭测试和开发测试,都通过了之后才提交的正式发布,所有信息都填写准确,google 也很快的通过了审核。

一些经验

虽然说上架Google play越来越严格,但是相比与国内来说还是容易很多的,国内上架对于个人开发者极度的不友好,并且备案,软著,哪个都不是好搞的。

上架Google play我认为第一条原则就是诚实,填写资料要真实,不仅仅是开发者信息如此,同时应用使用到的权限,收集的用户资料,等等都要如实填写,不可挂羊头卖狗肉。也要避免给审核人员看到的只有某一个功能,实际应用内有很多的功能会在审核后对用户开放。如果应用需要登录,最后要提供账号密码给google 的审核人员,对于非账号登录的,如加密货币钱包应用可以提供助记词或者操作指导的视频等。

另外,上架的应用应用做到尽量少的用户信息收集和权限获取。比如获取用户位置,如果不是主要功能,尽量不要获取。对于一些权限,如读写相册,相机权限等,在新版本系统中有提供不使用这些权限,直接通过系统的API实现的方式,也最好不要请求这些权限。对于DeviceId现在已经不允许收集了,对于Phone_State, 广告Id等,也应该尽量做到不要收集。

谷歌现在每年都要求应用升级Target 版本,这一点开发者也是需要去乖乖的做的,否者新应用无法上架,老应用无法更新。除此之外,对于Android的新特性开发者也应用去积极适配,对于一些特性,google play console也会提醒开发者去适配,比如下图所示。

对于上架的应用,即使没有发布新版本,也有可能被抽查去审核,这时候如果遇到了问题,谷歌也会发信息来让你整改,因此需要关注后台和邮箱,遇到问题要在最终截止日之前修掉对应问题,否者真的会被下架。我就遇到了这个问题,我所提供的登录凭据,审核人员自己输入错误,把我填写的o输入成了0,导致他无法登录,就给我发了整改通知。不过我在修复之后,错误提醒过了仍然没有消失,对于这一点,如果你已经确保改过了就不用再担心了。以下是错误提醒,过了这么多天仍然还在显示。

老账号的价值很高,审核也会比新账号更容易过,因此如果你有一个老账号,请保管好。

对于权限和隐私方面的检查,可以使用Google做的一款工具Checks,它可以帮助检查app中使用到的权限,请求的网络,同时还能够审查隐私政策文件,在发布前借助这个工具检查可以很大程度减少应用审核被拒的风险。

最后

从事Android开发10余年,大多数时间也都是做的海外应用,也是经历过了很多次google play被拒,审核的政策一直在变,之前能通过不代表现在也能通过审核,因此需要不断的学习google play的政策文件。

最后的最后,宣传一下我开发的Memos客户端应用fymemos,欢迎到google play下载。也欢迎留言交流应用上架的故事。

值得反复学习的google 文档:

  1. Play Academy
  2. Play 开发者政策中心
  3. Play Console Help Center

看完评论一下吧

使用Cuttlefish运行自编译Android固件

2024年11月21日 16:13

最近把本地的Android源码升级到了最新的Android 15,用于看Android源码的Android Studio for Platform也升级到了最新版本,Google的Cuttlefish最近发布了1.0版本,也顺便折腾了一下使用Cuttlefish来运行自己编译的Android系统,这里就介绍一下如何使用和遇到的问题。

Cuttlefish是什么

Cuttlefish是谷歌推出的一种可以配置的虚拟Android设备,它可以运行在我们本地设备上,也可以运行在服务器上面,官方也提供了Docker运行的支持,理论上可以运行在本地或者服务器的Debian设备上,或者运行在Google Compute Engine上。

用官方的化来说,它是一个更接近真实设备的Android模拟器,除了硬件抽象层(HAL)之外,它和实体设备的功能表现基本上是一致的。使用它来做CTS测试,持续集成测试会有更高的保真度。

在命令行中运行它,是没有类似模拟器的UI的,我们可以通过两种方式看到它的UI,一种是通过ADB连接,另一种则是开启它的webrtc功能,在浏览器中查看和交互。而他的虚拟硬件功能,可以让我们模拟多个屏幕,测试蓝牙wifi等各种功能。

安装Cuttlefish,编译Android固件

首先我们需要检查我们的设备是否支持KVM虚拟化,使用下面的命令:

1
grep -c -w "vmx\|svm" /proc/cpuinfo

如果得到一个非0的值,就是支持的。

之后我们需要有一个Android固件,可以选择去Android持续集成网站下载他们编译好的固件,也可以自己编译固件。下载固件要注意下载设备目标中带cf的,并且下载的目标CPU需要和需要运行的宿主机CPU架构一样,ARM就下载ARM的,X86就下载X86_64的,具体的操作可以看官方教程。我这里则是自己编译,使用如下代码设备我要编译的选项:

1
lunch aosp_cf_x86_64_phone-trunk_staging-eng

这样有了固件,还是不能够运行的。我们还需要去编译Cuttlefish,在https://github.com/google/android-cuttlefish下载源码后,在cuttlefish源码目录下执行如下代码编译和Android:

1
2
3
tools/buildutils/build_packages.sh
sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f
sudo dpkg -i ./cuttlefish-user_*_*64.deb || sudo apt-get install -f

如果你很幸运的化,上面会一次成功,但是我不是个幸运儿。于是了类似如下的错误:

1
While resolving toolchains for target //src/tools/ak/generatemanifest:generatemanifest (6312974): invalid registered toolchain '@local_jdk//:bootstrap_runtime_toolchain_definition': no such target '@local_jdk//:bootstrap_runtime_toolchain_definition': target 'bootstrap_runtime_toolchain_definition' not declared in package '' defined by /home/sam/.cache/bazel/_bazel_jcater/ddb4e20e0e2e6bca92f5deeef02ce168/external/local_jdk/BUILD.bazel (Tip: use `query "@local_jdk//:*"` to see all the targets in that package)

这个错误的原因呢,就是因为编译cuttlefish的时候使用了bazel这个构建工具,它依赖JDK,而我没有设置JAVA_HOME这个环境变量,因此把它加入到环境变量中就好了。类似如下:

export JAVA_HOME=/usr/lib/jvm/zulu-17-amd64

设置完成之后在Cuttlefish项目目录用如下命令检查一下,看看JAVA_HOME是否设置正确:

1
bazel info java-home

但是搞完之后,在安装这两个deb文件的时候又遇到了问题,告诉我我电脑上的grub-common签名有错误,这个呢是因为我之前添加了铜豌豆的软件源,grub升级的时候升级了铜豌豆的grub软件包,它和ubuntu官方的不同,于是卸载掉铜豌豆软件源,grub-common也重新安装,之后就没问题了。 这些做完之后,我们执行下面的命令设置环境,并且重启电脑就好了。

1
2
sudo usermod -aG kvm,cvdnetwork,render $USER
sudo reboot

使用Cuttlefish

在我们的已经编译完Android系统目录中首先执行如下代码让环境初始化好:

1
2
source ./build/envsetup.sh
lunch aosp_cf_x86_64_phone-trunk_staging-eng

随后执行如下的命令就可以启动Cuttlefish运行Android了:

1
launch_cvd --daemon

如果你是从Android官方下载的,那么会和我这有一些区别,可以去看一下官方教程。

这个时候我们就可以通过adb看看设备是否已经启动了,也可以在浏览器中打开,在本机浏览其打开使用如下地址和端口:

https://localhost:8443/

地址一定要使用https,点击左侧的swtich按钮就可以看到UI了。 webrtc是默认打开的,关于它的命令行更多使用方式可以查看官方文档,可以使用如下的命令查看。

1
launch --help

而关闭Cuttlefish,也很简单,使用如下的命令:

1
stop_cvd

新版Android Studio for Platform使用

2023版本的Android Studio for Platform(以下简称Asfp)在打开的时候是有一个单独的Open Aosp project选项的,而新版本的这个选项去掉了。刚刚使用它的时候我还一脸懵逼,测试了Import和Open都不行,结果最后发现新版的New选项就直接是导入Aosp工程了。

使用方式如下图。

我们可以根据上图选择我们需要导入的Module,选择Asfp给我们生成的项目文件存放的位置,之后Asfp会执行lunch的操作和它需要的一些依赖构建。在我们选定的目录下面也会生成一个asfp-config.json文件,它就是我们的项目设置,如果我们以后有变化了(比如想看不同的模块的代码),也可以直接修改这个文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
 "repoRoot" : "/home/sam/android/android-source",
 "modulePaths" : [
 "frameworks",
 "packages/inputmethods"
 ],
 "lunchTarget" : "aosp_cf_x86_64_phone-trunk_staging-eng",
 "nativeConfig" : {
 "excludePaths" : [ ],
 "excludeGenPaths" : [ ]
 },
 "syncConfig" : {
 "environmentVars" : { },
 "buildFlags" : [ ]
 }}

参考内容和资料:

  1. Cuttlefish 官方文档: https://source.android.com/docs/devices/cuttlefish
  2. Cuttlefish官方Repo: https://github.com/google/android-cuttlefish
  3. Bazel用户指南:https://bazel.build/docs/user-manual
  4. Android Cuttlefish emulator: https://2net.co.uk/blog/cuttlefish-android12.html

看完评论一下吧

Android源码分析:广播接收器注册与发送广播流程解析

2024年10月17日 19:40

广播,顾名思义就是把一个信息传播出去,在Android中也提供了广播和广播接收器BroadcastReceiver,用来监听特定的事件和发送特定的消息。不过广播分为全局广播和本地广播,本地广播是在Android Jetpack库中所提供,其实现也是基于Handler和消息循环机制,并且这个类Android官方也不推荐使用了。我们这里就来看看Android全局的这个广播。

应用开发者可以自己发送特定的广播,而更多场景则是接收系统发送的广播。注册广播接收器有在AndroidManifest文件中声明和使用代码注册两种方式,在应用的target sdk大于等于Android 8.0(Api Version 26)之后,系统会限制在清单文件中注册。通过清单方式注册的广播,代码中没有注册逻辑,只有PMS中读取它的逻辑,我们这里不进行分析。

注册广播接收器

首先是注册广播接收器,一般注册一个广播接收器的代码如下:

1
2
3
val br: BroadcastReceiver = MyBroadcastReceiver()
val filter = IntentFilter(ACTION_CHARGING)
activity.registerReceiver(br, filter)

使用上面的代码就能注册一个广播接收器,当手机开始充电就会收到通知,会去执行MyBroadcastReceiveronReceive方法。

那我们就从这个registerReceiver来时往里面看,因为Activity是Context的子类,这个注册的方法的实现则是在ContextImpl当中,其中最终调用的方法为registerReceiverInternal,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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();
 }
 rd = mPackageInfo.getReceiverDispatcher(
 receiver, context, scheduler,
 mMainThread.getInstrumentation(), true);
 } else {
 ...
 }
 }
 try {
 ActivityThread thread = ActivityThread.currentActivityThread();
 Instrumentation instrumentation = thread.getInstrumentation();
 if (instrumentation.isInstrumenting()
 && ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
 flags = flags | Context.RECEIVER_EXPORTED;
 }
 final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
 mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
 AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
 flags);
 if (intent != null) {
 intent.setExtrasClassLoader(getClassLoader());
 intent.prepareToEnterProcess(
 ActivityThread.isProtectedBroadcast(intent),
 getAttributionSource());
 }
 return intent;
 } catch (RemoteException e) {
 ...
 }
}

我们在注册广播的时候只传了两个参数,但是实际上它还可以传不少的参数,这里userId就是注册的用户id,会被自动 填充成当前进程的用户Id,broadcastPermission表示这个广播的权限,也就是说需要有该权限的应用发送的广播,这个接收者才能接收到。scheduler就是一个Handler,默认不传,在第8行可以看到,会拿当前进程的主线程的Handlerflag是广播的参数,这里比较重要的就是RECEIVER_NOT_EXPORTED,添加了它则广播不会公开暴露,其他应用发送的消息不会被接收。

在第10行,这里创建了一个广播的分发器,在24行,通过AMS去注册广播接收器,只有我们的broadcast会用到contentprovider或者有sticky广播的时候,30行才会执行到,这里跳过。

获取广播分发器

首先来看如何获取广播分发器,这块的代码在LoadedApk.java中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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();
 }
}

先来说一下mReceivers,它的结构为ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>,也就是嵌套了两层的ArrayMap,外层是以Context为key,内层以Receiver为key,实际存储的为ReceiverDispatcherReceiverDispatcher内部所放的IIntentReceiver比较重要,也就是我们这个方法所返回的值,它实际是IIntentReceiver.Stub,也就是它的Binder实体类。

这段代码的逻辑也比较清晰,就是根据ContextReceiver到map中去查找看是否之前注册过,如果注册过就已经有这个Dispatcher了,如果没有就创建一个,并且放到map中去,最后返回binder对象出去。

AMS注册广播接收器

在AMS注册的代码很长,我们这里主要研究正常的普通广播注册,关于黏性广播,instantApp的广播,以及广播是否导出等方面都省略不予研究。以下为我们关注的核心代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
 String callerFeatureId, String receiverId, IIntentReceiver receiver,
 IntentFilter filter, String permission, int userId, int flags) {
 ...
 synchronized(this) {
 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.mReceivers.numberOfReceivers();
 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.mReceivers.addReceiver(rl);
 } else {
 try {
 receiver.asBinder().linkToDeath(rl, 0);
 } catch (RemoteException e) {
 return sticky;
 }
 rl.linkedToDeath = true;
 }
 mRegisteredReceivers.put(receiver.asBinder(), rl);
 } else {
 // 处理userId, uid,pid 等不同的错误
 }

 BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
 receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
 exported);
 if (rl.containsFilter(filter)) {
 } else {
 rl.add(bf);
 mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
 }
 }
 ...
}

在前面ContextImpl中调用AMS注册Reciever的地方,我们传的就是Receiver的Binder实体,这里拿到的是binder引用。在代码中我们可以看到,首先会以我们传过来的receiver的binder对象为key,到mRegisterReceivers当中去获取ReceiverList,这里我们就知道receiver在System_server中是怎样存储的了。如果AMS当中没有,会去创建一个ReceiverList并放置到这个map当中去,如果存在则不需要做什么事情。但是这一步只是放置了Receiver,而我们的Receiver对应的关心的IntentFilter还没使用,这里就需要继续看31行的代码了。在这里这是使用了我们传过来的IntentFilter创建了一个BroadcastFilter对象,并且把它放到了ReceiverList当中,同时还放到了mReceiverResolver当中,这个对象它不是一个Map而是一个IntentResolver,其中会存储我们的BroadcastFilter,具体这里先不分析了。 BroadcastReceiver 存放结构

到这里我们就看完了广播接收器的注册,在App进程和System_Server中分别将其存储,具体两边的数据结构如上图所示。这里可以继续看看发送广播的流程了。

发送广播

一般我们发送广播会调用如下的代码:

1
2
3
4
5
Intent().also { intent -> 
 intent.setAction("com.example.broadcast.MY_NOTIFICATION") 
 intent.putExtra("data", "Nothing to see here, move along.") 
 activity.sendBroadcast(intent)
}

我们通过设置Action来匹配对应的广播接收器,通过设置Data或者Extra,这样广播接收器中可以接收到对应的数据,最后调用sendBroadcast来发送。而sendBroadcast的实现也是在ContextImpl中,源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Override
public void sendBroadcast(Intent intent) {
 warnIfCallingFromSystemProcess();
 String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
 try {
 intent.prepareToLeaveProcess(this);
 ActivityManager.getService().broadcastIntentWithFeature(
 mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
 null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
 null, AppOpsManager.OP_NONE, null, false, false, getUserId());
 } catch (RemoteException e) {
 throw e.rethrowFromSystemServer();
 }
}

这里代码比较简单,就是直接调用AMS的broadcastIntentWithFeature来发送广播。

AMS发送广播

这里我们可以直接看AMS中的broadcastIntentWithFeature的源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
 Intent intent, String resolvedType, IIntentReceiver resultTo,
 int resultCode, String resultData, Bundle resultExtras,
 String[] requiredPermissions, String[] excludedPermissions,
 String[] excludedPackages, int appOp, Bundle bOptions,
 boolean serialized, boolean sticky, int userId) {
 enforceNotIsolatedCaller("broadcastIntent");
 synchronized(this) {
 intent = verifyBroadcastLocked(intent);

 final ProcessRecord callerApp = getRecordForAppLOSP(caller);
 final int callingPid = Binder.getCallingPid();
 final int callingUid = Binder.getCallingUid();

 final long origId = Binder.clearCallingIdentity();
 try {
 return broadcastIntentLocked(callerApp,
 callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
 intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
 requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
 serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId);
 } finally {
 Binder.restoreCallingIdentity(origId);
 }
 }
}

第10行代码,主要验证Intent,比如检查它的Flag,检查它是否传文件描述符之类的,里面的代码比较简单清晰,这里不单独看了。后面则是获取调用者的进程,uid,pid之类的,最后调用broadcastIntentLocked,这个方法的代码巨多,接近1000行代码,我们同样忽略sticky的广播,也忽略顺序广播,然后来一点一点的看:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//ActivityManagerService.java 
//final int broadcastIntentLocked(...)
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
 ALLOW_NON_FULL, "broadcast", callerPackage);
final String action = intent.getAction();

首先这里的代码是对Intent做一下封装,并且如果系统还在启动,不允许启动应用进程,以及获取当前的用户ID,大部分情况下,我们只需要考虑一个用户的情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
if (action != null) {
 ...
 switch (action) {
 ...
 case Intent.ACTION_PACKAGE_DATA_CLEARED:
 {
 Uri data = intent.getData();
 String ssp;
 if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
 mAtmInternal.onPackageDataCleared(ssp, userId);
 }
 break;
 }
 case Intent.ACTION_TIMEZONE_CHANGED:
 mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
 break;
 ...
 }
}

对于一些系统的广播事件,除了要发送广播给应用之外,在AMS中,还会根据其广播,来调用相关的服务或者执行相关的逻辑,也会在这里调用其代码。这里我罗列了清除应用数据和时区变化两个广播,其他的感兴趣的可以自行阅读相关代码。

1
2
3
4
5
6
int[] users;
if (userId == UserHandle.USER_ALL) {
 users = mUserController.getStartedUserArray();
} else {
 users = new int[] {userId};
}

以上代码为根据前面拿到的userId,来决定广播要发送给所有人还是仅仅发送给当前用户,并且把userId保存到users数组当中。

获取广播接收者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
 receivers = collectReceiverComponents(
 intent, resolvedType, callingUid, users, broadcastAllowList);
}
if (intent.getComponent() == null) {
 final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
 if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
 ...
 } else {
 registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
 resolvedType, false /*defaultOnly*/, userId);
 }
}

以上为获取我们注册的所有的接收器的代码,其中FLAG_RECEIVER_REGISTERED_ONLY意味着仅仅接收注册过的广播,前面在判断当前系统还未启动完成的时候有添加这个FLAG,其他情况一般不会有这个Flag,这里我们按照没有这个flag处理。那也就会执行第4行的代码。另外下面还有从mReceiverResolver从获取注册的接收器的代码,因为大部分情况不是从shell中执行的,因此也忽略了其代码。

首先看collectReceiverComponents的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
 int callingUid, int[] users, int[] broadcastAllowList) {
 int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;

 List<ResolveInfo> receivers = null;
 HashSet<ComponentName> singleUserReceivers = null;
 boolean scannedFirstReceivers = false;
 for (int user : users) {
 List<ResolveInfo> newReceivers = mPackageManagerInt.queryIntentReceivers(
 intent, resolvedType, pmFlags, callingUid, user, true /* forSend */); //通过PMS,根据intent和uid读取Manifest中注册的接收器
 if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
 for (int i = 0; i < newReceivers.size(); i++) {
 ResolveInfo ri = newReceivers.get(i);
 //如果调用不是系统用户,移除只允许系统用户接收的接收器
 if ((ri.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
 newReceivers.remove(i);
 i--;
 }
 }
 }
 // 把别名替换成真实的接收器 
 if (newReceivers != null) {
 for (int i = newReceivers.size() - 1; i >= 0; i--) {
 final ResolveInfo ri = newReceivers.get(i);
 final Resolution<ResolveInfo> resolution =
 mComponentAliasResolver.resolveReceiver(intent, ri, resolvedType,
 pmFlags, user, callingUid, true /* forSend */);
 if (resolution == null) {
 // 未找到对应的接收器,删除这个记录 
 newReceivers.remove(i);
 continue;
 }
 if (resolution.isAlias()) {
 //找到对应的真实的接收器,就把别名的记录替换成真实的目标
 newReceivers.set(i, resolution.getTarget());
 }
 }
 }
 if (newReceivers != null && newReceivers.size() == 0) {
 newReceivers = null;
 }

 if (receivers == null) {
 receivers = newReceivers;
 } else if (newReceivers != null) {
 if (!scannedFirstReceivers) {
 //查找单用户记录的接收器,并且保存
 scannedFirstReceivers = true;
 for (int i = 0; i < receivers.size(); i++) {
 ResolveInfo ri = receivers.get(i);
 if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
 ComponentName cn = new ComponentName(
 ri.activityInfo.packageName, ri.activityInfo.name);
 if (singleUserReceivers == null) {
 singleUserReceivers = new HashSet<ComponentName>();
 }
 singleUserReceivers.add(cn);
 }
 }
 }
 for (int i = 0; i < newReceivers.size(); i++) {
 ResolveInfo ri = newReceivers.get(i);
 if ((ri.activityInfo.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
 ComponentName cn = new ComponentName(
 ri.activityInfo.packageName, ri.activityInfo.name);
 if (singleUserReceivers == null) {
 singleUserReceivers = new HashSet<ComponentName>();
 }
 if (!singleUserReceivers.contains(cn)) {
 //对于单用户的接收器,只存一次到返回结果中
 singleUserReceivers.add(cn);
 receivers.add(ri);
 }
 } else {
 receivers.add(ri);
 }
 }
 }
 }
 ...
 return receivers;
}

以上就根据信息通过PMS获取所有通过Manifest静态注册的广播接收器,对其有一些处理,详见上面的注释。

对于我们在代码中动态注册的接收器,则需要看mReceiverResolver.queryIntent的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
 String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
 ArrayList<R> finalList = new ArrayList<R>();
 F[] firstTypeCut = null;
 F[] secondTypeCut = null;
 F[] thirdTypeCut = null;
 F[] schemeCut = null;

 if (resolvedType == null && scheme == null && intent.getAction() != null) {
 firstTypeCut = mActionToFilter.get(intent.getAction());
 }

 FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
 Computer computer = (Computer) snapshot;
 if (firstTypeCut != null) {
 buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
 scheme, firstTypeCut, finalList, userId, customFlags);
 }
 sortResults(finalList); //按照IntentFilter的priority优先级降序排序
 return finalList;
}

以上代码中,这个mActionToFilter就是我们前面注册广播时候,将BroadcastFilter添加进去的一个ArrayMap,这里会根据Action去其中取出所有的BroadcastFilter,之后调用buildResolveList将其中的不符合本次广播接收要求的广播接收器给过滤掉,最后按照IntentFilter的优先级降序排列。

到这里我们就有两个列表receivers存放Manifest静态注册的将要本次广播接收者,和registeredReceivers通过代码手动注册的广播接收者。

广播入队列

首先来看通过代码注册的接收器不为空,并且不是有序广播的情况,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
 ...
 final BroadcastQueue queue = broadcastQueueForIntent(intent);
 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
 callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
 requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
 registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
 sticky, false, userId, allowBackgroundActivityStarts,
 backgroundActivityStartsToken, timeoutExempt);
 ...
 final boolean replaced = replacePending
 && (queue.replaceParallelBroadcastLocked(r) != null);
 if (!replaced) {
 queue.enqueueParallelBroadcastLocked(r);
 queue.scheduleBroadcastsLocked();
 }
 registeredReceivers = null;
 NR = 0;
}

在这里,第4行会首先根据intent的flag获取对应的BroadcastQueue,这里有四个Queue,不看其代码了,不过逻辑如下:

  1. 如果有FLAG_RECEIVER_OFFLOAD_FOREGROUND 标记,则使用mFgOffloadBroadcastQueue
  2. 如果当前开启了offloadQueue,也就是mEnableOffloadQueue,并且有FLAG_RECEIVER_OFFLOAD标记,则使用mBgOffloadBroadcastQueue
  3. 如果有FLAG_RECEIVER_FOREGROUND,也就是前台时候才接收广播,则使用mFgBroadcastQueue
  4. 如果没有上述标记,则使用mBgBroadcastQueue。 拿到queue之后,会创建一条BroadcastRecord,其中会记录传入的参数,intent,以及接收的registeredReceivers,调用queue的入队方法,最后把registeredReceivers设置为null,计数也清零。具体入队的代码,我们随后再看,这里先看其他情况下的广播入队代码。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int ir = 0;
if (receivers != null) {
 String skipPackages[] = null;
 //对于添加应用,删除应用数据之类的广播,不希望变化的应用能够接收到对应的广播
 //这里设置忽略它们
 if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
 || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
 || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
 Uri data = intent.getData();
 if (data != null) {
 String pkgName = data.getSchemeSpecificPart();
 if (pkgName != null) {
 skipPackages = new String[] { pkgName };
 }
 }
 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
 skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
 }
 if (skipPackages != null && (skipPackages.length > 0)) {
 //如果Manifest注册的广播接收器的包名和skip的一样,那就移除它们
 for (String skipPackage : skipPackages) {
 if (skipPackage != null) {
 int NT = receivers.size();
 for (int it=0; it<NT; it++) {
 ResolveInfo curt = (ResolveInfo)receivers.get(it);
 if (curt.activityInfo.packageName.equals(skipPackage)) {
 receivers.remove(it);
 it--;
 NT--;
 }
 }
 }
 }
 }

 int NT = receivers != null ? receivers.size() : 0;
 int it = 0;
 ResolveInfo curt = null;
 BroadcastFilter curr = null;
 while (it < NT && ir < NR) {
 if (curt == null) {
 curt = (ResolveInfo)receivers.get(it);
 }
 if (curr == null) {
 curr = registeredReceivers.get(ir);
 }
 if (curr.getPriority() >= curt.priority) {
 //如果动态注册的广播优先级比静态注册的等级高,就把它添加到静态注册的前面。
 receivers.add(it, curr);
 ir++;
 curr = null;
 it++;
 NT++;
 } else {
 // 如果动态注册的广播优先级没有静态注册的等级高,那就移动静态注册的游标,下一轮在执行相关的判断。
 it++;
 curt = null;
 }
 }
}
while (ir < NR) { //如果registeredReceivers中的元素没有全部放到receivers里面,就一个一个的遍历并放进去。
 if (receivers == null) {
 receivers = new ArrayList();
 }
 receivers.add(registeredReceivers.get(ir));
 ir++;
}

以上的代码所做的事情就是首先移除静态注册的广播当中需要忽略的广播接收器,随后将静态注册和动态注册的广播接收器,按照优先级合并到同一个列表当中,当然如果动态注册的前面已经入队过了,这里实际上是不会在合并的。关于合并的代码,就是经典的两列表合并的算法,具体请看代码和注释。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
if ((receivers != null && receivers.size() > 0)
 || resultTo != null) {
 BroadcastQueue queue = broadcastQueueForIntent(intent);
 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
 callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
 requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
 receivers, resultTo, resultCode, resultData, resultExtras,
 ordered, sticky, false, userId, allowBackgroundActivityStarts,
 backgroundActivityStartsToken, timeoutExempt);

 final BroadcastRecord oldRecord =
 replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
 if (oldRecord != null) {
 if (oldRecord.resultTo != null) {
 final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
 try {
 oldRecord.mIsReceiverAppRunning = true;
 oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
 oldRecord.intent,
 Activity.RESULT_CANCELED, null, null,
 false, false, oldRecord.userId, oldRecord.callingUid, callingUid,
 SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
 } catch (RemoteException e) {

 }
 }
 } else {
 queue.enqueueOrderedBroadcastLocked(r);
 queue.scheduleBroadcastsLocked();
 }
}else {
 //对于无人关心的广播,也做一下记录
 if (intent.getComponent() == null && intent.getPackage() == null
 && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
 addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
 }
}

以上的代码,跟前面入队的代码也差不多,不过这里如果采用的方法是enqueueOrderedBroadcastLocked,并且多了关于已经发送的广播的替换的逻辑,这里我们先不关注。如果receivers为空,并且符合条件的隐式广播,系统也会对其进行记录,具体,我们这里也不进行分析了。

BroadcastQueue 入队

我们知道前面入队的时候有两个方法,分别是enqueueParallelBroadcastLockedenqueueOrderedBroadcastLocked,我们先来分析前者。

1
2
3
4
5
6
7
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
 r.enqueueClockTime = System.currentTimeMillis();
 r.enqueueTime = SystemClock.uptimeMillis();
 r.enqueueRealTime = SystemClock.elapsedRealtime();
 mParallelBroadcasts.add(r);
 enqueueBroadcastHelper(r);
}

这里就是将BroadcastRecord放到mParallelBroadcasts列表中,随后执行enqueueBroadcastHelper,我们先看继续看一下enqueueOrderedBroadcastLocked方法。

1
2
3
4
5
6
7
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
 r.enqueueClockTime = System.currentTimeMillis();
 r.enqueueTime = SystemClock.uptimeMillis();
 r.enqueueRealTime = SystemClock.elapsedRealtime();
 mDispatcher.enqueueOrderedBroadcastLocked(r);
 enqueueBroadcastHelper(r);
}

这里跟上面很类似,差别是这里把BroadcastRecord入队了mDispatcher,对于普通广播,其内部是把这个记录放到了mOrderedBroadcasts列表。 而enqueueBroadcastHelper方法仅仅用于trace,我们这里不需要关注。

到了这里,我们把广播放到对应的列表了,但是广播还是没有分发出去。

AMS端广播的分发

以上是代码入了BroadcastQueu,接下来就可以看看队列中如何处理它了。首先需要注意一下,记录在入队的同时还调用了BroadcastQueuescheduleBroadcastsLock方法,代码如下:

1
2
3
4
5
6
7
public void scheduleBroadcastsLocked() {
 if (mBroadcastsScheduled) {
 return;
 }
 mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
 mBroadcastsScheduled = true;
}

这里使用了Handler发送了一条BROADCAST_INTENT_MSG消息,我们可以去看一下BroadcastHandlerhandleMessage方法。其中在处理这个消息的时候调用了processNextBroadcast方法,我们可以直接去看其实现:

1
2
3
4
5
private void processNextBroadcast(boolean fromMsg) {
 synchronized (mService) {
 processNextBroadcastLocked(fromMsg, false);
 }
}

这里开启了同步块调用了processNextBroadcastLocked方法,这个方法依然很长,其中涉及到广播的权限判断,对于静态注册的广播,可能还涉及到对应进程的启动等。

动态广播的分发

动态注册的无序广播相对比较简单,这里我们仅仅看一下其中无序广播的分发处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
if (fromMsg) {
 mBroadcastsScheduled = false; //通过handleMessage过来,把flag设置为false
}
while (mParallelBroadcasts.size() > 0) {
 r = mParallelBroadcasts.remove(0);
 r.dispatchTime = SystemClock.uptimeMillis();
 r.dispatchRealTime = SystemClock.elapsedRealtime();
 r.dispatchClockTime = System.currentTimeMillis();
 r.mIsReceiverAppRunning = true;
 final int N = r.receivers.size();

 for (int i=0; i<N; i++) {
 Object target = r.receivers.get(i);

 deliverToRegisteredReceiverLocked(r,
 (BroadcastFilter) target, false, i); //分发
 }
 addBroadcastToHistoryLocked(r); //把广播添加的历史记录中
}


这里就是遍历`ParallelBroadcasts`中的每一条`BroadcastRecord`,其中会再分别遍历每一个`BroadcastFilter`,调用`deliverToRegisteredReceiverLocked`来分发广播
```java
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
 BroadcastFilter filter, boolean ordered, int index) {
 boolean skip = false;
 ...

 if (filter.requiredPermission != null) {
 int perm = mService.checkComponentPermission(filter.requiredPermission,
 r.callingPid, r.callingUid, -1, true);
 if (perm != PackageManager.PERMISSION_GRANTED) {
 skip = true;
 } else {
 final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
 if (opCode != AppOpsManager.OP_NONE
 && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
 r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
 != AppOpsManager.MODE_ALLOWED) {
 skip = true;
 }
 }
 }
 ...
 if (skip) {
 r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
 return;
 }

 r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
 ...
 try {

 if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
 if (ordered) {
 skipReceiverLocked(r);
 }
 } else {
 r.receiverTime = SystemClock.uptimeMillis();
 maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
 maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
 maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
 new Intent(r.intent), r.resultCode, r.resultData,
 r.resultExtras, r.ordered, r.initialSticky, r.userId,
 filter.receiverList.uid, r.callingUid,
 r.dispatchTime - r.enqueueTime,
 r.receiverTime - r.dispatchTime);
 if (filter.receiverList.app != null
 && r.allowBackgroundActivityStarts && !r.ordered) {
 postActivityStartTokenRemoval(filter.receiverList.app, r);
 }
 }
 if (ordered) {
 r.state = BroadcastRecord.CALL_DONE_RECEIVE;
 }
 } catch (RemoteException e) {
 ...
 if (ordered) {
 r.receiver = null;
 r.curFilter = null;
 filter.receiverList.curBroadcast = null;
 }
 }
}

在这个方法中有大段的代码是判断是否需要跳过当前这个广播,我这里仅仅保留了几句权限检查的代码。对于跳过的记录会将其BroadcastRecorddelivery[index]值设置为DELIVERY_SKIPPED, 而成功分发的会设置为DELIVERY_DELIVERED。对于有序广播的分发我们这里也不予分析,直接看无序广播的分发,在分发之前会尝试给对应的接收进程添加后台启动Activity的权限,这个会在分发完成之后恢复原状,调用的是maybeAddAllowBackgroundActivityStartsToken,就不具体分析了。

之后会调用performReceiveLocked去进行真正的分发,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
 Intent intent, int resultCode, String data, Bundle extras,
 boolean ordered, boolean sticky, int sendingUser,
 int receiverUid, int callingUid, long dispatchDelay,
 long receiveDelay) throws RemoteException {
 if (app != null) {
 final IApplicationThread thread = app.getThread();
 if (thread != null) {
 try {
 thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
 data, extras, ordered, sticky, sendingUser,
 app.mState.getReportedProcState());
 } catch (RemoteException ex) {
 ...
 throw ex;
 }
 } else {
 ...
 throw new RemoteException("app.thread must not be null");
 }
 } else {
 receiver.performReceive(intent, resultCode, data, extras, ordered,
 sticky, sendingUser);
 }
 ...
}

在执行分发的代码中,如果我们的ProcessRecord不为空,并且ApplicationThread也存在的情况下,会调用它的scheduleRegisterReceiver方法。如果进程记录为空,则会直接使用IIntentReceiverperformReceiver方法。我们在App中动态注册的情况,ProcessRecord一定是不为空的,我们也以这种情况继续向下分析。

动态注册广播分发App进程逻辑

1
2
3
4
5
6
7
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);
}

在应用进程中,首先也只是根据AMS传过来的processState更新一下进程的状态,随后还是调用了IIntentReceiverperformReceive方法,performReceiveLoadedApk当中,为内部类InnerReceiver的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void performReceive(Intent intent, int resultCode, String data,
 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
 final LoadedApk.ReceiverDispatcher rd;
 if (intent == null) {
 rd = null;
 } else {
 rd = mDispatcher.get(); //获取ReceiverDispatcher
 }
 if (rd != null) {
 rd.performReceive(intent, resultCode, data, extras,
 ordered, sticky, sendingUser);
 } else {
 IActivityManager mgr = ActivityManager.getService();
 try {
 if (extras != null) {
 extras.setAllowFds(false);
 }
 mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
 } catch (RemoteException e) {
 throw e.rethrowFromSystemServer();
 }
 }
}

在应用进程中,首先会获取ReceiverDisptcher,这个一般不会为空。但是系统代码比较严谨,也考虑了,不存在的情况会调用AMS的finishReceiver完成整个流程。

对于存在的情况,会调用ReceiverDispatcherperformReceive方法继续分发。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public void performReceive(Intent intent, int resultCode, String data,
 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
 final Args args = new Args(intent, resultCode, data, extras, ordered,
 sticky, sendingUser);
 ..
 if (intent == null || !mActivityThread.post(args.getRunnable())) {
 if (mRegistered && ordered) {
 IActivityManager mgr = ActivityManager.getService();
 ..
 args.sendFinished(mgr);
 }
 }
}

这里的代码有点绕,不过也还比较清晰,首先是创建了一个Args对象,之后根据java的语法,如果intent不为空的时候会执行如下代码:

1
mActivityThread.post(args.getRunnable())

当这个执行失败的时候,才会看情况执行8行到第10行的代码。而这个Runnable就是应用端真正分发的逻辑,其代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public final Runnable getRunnable() {
 return () -> {
 final BroadcastReceiver receiver = mReceiver;
 final boolean ordered = mOrdered;


 final IActivityManager mgr = ActivityManager.getService();
 final Intent intent = mCurIntent;

 mCurIntent = null;
 mDispatched = true;
 mRunCalled = true;
 if (receiver == null || intent == null || mForgotten) {
 ...
 return;
 }
 try {
 ClassLoader cl = mReceiver.getClass().getClassLoader();
 intent.setExtrasClassLoader(cl);
 intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
 mContext.getAttributionSource());
 setExtrasClassLoader(cl);
 receiver.setPendingResult(this);
 receiver.onReceive(mContext, intent);
 } catch (Exception e) {
 if (mRegistered && ordered) {
 sendFinished(mgr);
 }
 if (mInstrumentation == null ||
 !mInstrumentation.onException(mReceiver, e)) {
 throw new RuntimeException(
 "Error receiving broadcast " + intent
 + " in " + mReceiver, e);
 }
 }

 if (receiver.getPendingResult() != null) {
 finish();
 }
 };
}

这里的receiver就是我们注册时候的那个BroadcastReceiver,这里将当前的Args对象作为它的PendingResult,在这里调用了它的onReceive方法 ,最后看pendingResult是否为空,不为空则调用PendingResultfinish()方法。当我们在onReceive中编写代码的时候,如果调用了goAsync的话,那这里的PendingResult就会为空。

另外就是我们这个Runnable是使用的mActivityThread的post方法投递出去的,它是一个Handler对象,它是在注册广播接收器的时候指定的,默认是应用的主线程Handler,也就是说广播的执行会在主线程。

但是即使是我们使用goAsync的话,处理完成之后也是需要手动调用finish的,我们后面在来看相关的逻辑。

静态广播的发送

在前面分析的BroadcastQueueprocessNextBroadcastLocked方法中,我们只分析了动态广播的发送,这里再看一下静态广播的发送,首先仍然是看processNextBroadcastLocked中的相关源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
BroadcastRecord r;
do {
 r = mDispatcher.getNextBroadcastLocked(now);
 if (r == null) {
 ...
 return;
 }
 ...

} while(r === null);
...
if (app != null && app.getThread() != null && !app.isKilled()) {
 try {
 app.addPackage(info.activityInfo.packageName,
 info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
 maybeAddAllowBackgroundActivityStartsToken(app, r);
 r.mIsReceiverAppRunning = true;
 processCurBroadcastLocked(r, app);
 return;
 } catch(RemoteException e) {
 ...
 }
}
...

在第3行,会从mDispatcher中拿BroadcastRecord的记录,我们之前在AMS端入队的代码,对于静态注册的广播和有序广播都是放在mDispatcher当中的,这里拿到动态注册的有序广播也会从这里拿,它的后续逻辑跟前面分析的是一样的,这里不再看了。对于静态注册的广播,在调用后续的方法之前,需要先获取对应进程的ProcessRecord,和ApplicationThread,并且进行广播权限的检查,进程是否存活检查这些在我们11行的位置,都省略不看了。如果App进程存活则会走到我们12行的部分,否则会去创建对应的进程,创建完进程会再去分发广播。

动态注册的广播,会传一个IIntentReceiver的Binder到AMS,而静态注册的广播,我们跟着第18行代码processCurBroadcastLocked方法进去一览究竟:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private final void processCurBroadcastLocked(BroadcastRecord r,
 ProcessRecord app) throws RemoteException {
 final IApplicationThread thread = app.getThread();
 ...
 r.receiver = thread.asBinder();
 r.curApp = app;
 final ProcessReceiverRecord prr = app.mReceivers;
 prr.addCurReceiver(r);
 app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
 ...
 r.intent.setComponent(r.curComponent);

 boolean started = false;
 try {
 mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
 PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
 thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
 mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
 r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
 app.mState.getReportedProcState());
 started = true;
 } finally {
 if (!started) {
 r.receiver = null;
 r.curApp = null;
 prr.removeCurReceiver(r);
 }
 }

}

在这个方法中,把App的ProcessRecord放到了BroadcastRecord当中,并且把ApplicationThread设置为receiver,最后是调用了ApplicationThreadscheduleReceiver,从而通过binder调用App进程。

静态注册广播分发App进程逻辑

通过Binder调用,在App的ApplicationThread代码中,调用的是如下方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public final void scheduleReceiver(Intent intent, ActivityInfo info,
 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
 boolean sync, int sendingUser, int processState) {
 updateProcessState(processState, false);
 ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
 sync, false, mAppThread.asBinder(), sendingUser);
 r.info = info;
 r.compatInfo = compatInfo;
 sendMessage(H.RECEIVER, r);
}

这里是创建了一个ReceiverData把AMS传过来数据包裹其中,并且通过消息发出去,之后会调用ActivityThreadhandleReceiver方法, 代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
private void handleReceiver(ReceiverData data) {
 String component = data.intent.getComponent().getClassName();

 LoadedApk packageInfo = getPackageInfoNoCheck(
 data.info.applicationInfo, data.compatInfo);

 IActivityManager mgr = ActivityManager.getService();

 Application app;
 BroadcastReceiver receiver;
 ContextImpl context;
 try {
 app = packageInfo.makeApplicationInner(false, mInstrumentation);
 context = (ContextImpl) app.getBaseContext();
 if (data.info.splitName != null) {
 context = (ContextImpl) context.createContextForSplit(data.info.splitName);
 }
 if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
 final String attributionTag = data.info.attributionTags[0];
 context = (ContextImpl) context.createAttributionContext(attributionTag);
 }
 java.lang.ClassLoader cl = context.getClassLoader();
 data.intent.setExtrasClassLoader(cl);
 data.intent.prepareToEnterProcess(
 isProtectedComponent(data.info) || isProtectedBroadcast(data.intent),
 context.getAttributionSource());
 data.setExtrasClassLoader(cl);
 receiver = packageInfo.getAppFactory()
 .instantiateReceiver(cl, data.info.name, data.intent);
 } catch (Exception e) {
 data.sendFinished(mgr);
 ...
 }

 try {

 sCurrentBroadcastIntent.set(data.intent);
 receiver.setPendingResult(data);
 receiver.onReceive(context.getReceiverRestrictedContext(),
 data.intent);
 } catch (Exception e) {
 data.sendFinished(mgr);
 } finally {
 sCurrentBroadcastIntent.set(null);
 }

 if (receiver.getPendingResult() != null) {
 data.finish();
 }
}

这个代码中主要有两个try-catch的代码块,分别是两个主要的功能区。因为静态注册的广播,我们的广播接收器是没有构建的,AMS传过来的只是广播的类名,因此,第一块代码的功能就是创建广播接收器对象。第二块代码则是去调用广播接收器的onReceive方法,从而传递广播。另外这里会调用PendingResultfinish去执行广播处理完成之后的逻辑,以及告知AMS,不过这里的PendingResult就是前面创建的ReceiverData

完成广播的发送

在分析前面的动态注册广播分发和静态注册广播分发的时候,最终在App进程它们都有一个Data,静态为ReceiverData, 动态为Args,他们都继承了PendingResult,最终都会调用PendingResultfinish方法来完成后面的收尾工作,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public final void finish() {
 if (mType == TYPE_COMPONENT) {
 final IActivityManager mgr = ActivityManager.getService();
 if (QueuedWork.hasPendingWork()) {
 QueuedWork.queue(new Runnable() {
 @Override public void run() {
 sendFinished(mgr);
 }
 }, false);
 } else {
 sendFinished(mgr);
 }
 } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
 final IActivityManager mgr = ActivityManager.getService();
 sendFinished(mgr);
 }
}

这里的QueuedWork主要用于运行SharedPreferences写入数据到磁盘,当然这个如果其中有未运行的task则会添加一个Task到其中来运行sendFinished,这样做的目的是为了保证如果当前除了广播接收器没有别的界面或者Service运行的时候,AMS不会杀掉当前的进程。否则会直接运行sendFinished方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public void sendFinished(IActivityManager am) {
 synchronized (this) {
 if (mFinished) {
 throw new IllegalStateException("Broadcast already finished");
 }
 mFinished = true;
 try {
 if (mResultExtras != null) {
 mResultExtras.setAllowFds(false);
 }
 if (mOrderedHint) {
 am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
 mAbortBroadcast, mFlags);
 } else {
 am.finishReceiver(mToken, 0, null, null, false, mFlags);
 }
 } catch (RemoteException ex) {
 }
 }
}

这里就是调用AMS的finishReceiver方法,来告诉AMS广播接收的处理已经执行完了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public void finishReceiver(IBinder who, int resultCode, String resultData,
 Bundle resultExtras, boolean resultAbort, int flags) {
 if (resultExtras != null && resultExtras.hasFileDescriptors()) {
 throw new IllegalArgumentException("File descriptors passed in Bundle");
 }

 final long origId = Binder.clearCallingIdentity();
 try {
 boolean doNext = false;
 BroadcastRecord r;
 BroadcastQueue queue;

 synchronized(this) {
 if (isOnFgOffloadQueue(flags)) {
 queue = mFgOffloadBroadcastQueue;
 } else if (isOnBgOffloadQueue(flags)) {
 queue = mBgOffloadBroadcastQueue;
 } else {
 queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
 ? mFgBroadcastQueue : mBgBroadcastQueue;
 }

 r = queue.getMatchingOrderedReceiver(who);
 if (r != null) {
 doNext = r.queue.finishReceiverLocked(r, resultCode,
 resultData, resultExtras, resultAbort, true);
 }
 if (doNext) {
 }
 trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
 }

 } finally {
 Binder.restoreCallingIdentity(origId);
 }
}

相关的逻辑从13行开始,首先仍然是根据广播的flag找到之前的BroadcastQueue,之后根据IBinder找到发送的这一条BroadcastRecord,调用Queue的finishReceiverLocked方法。根据它的返回值,再去处理队列中的下一个广播记录。最后的trimApplicationsLocked里面会视情况来决定是否停止App进程,我们这里就不进行分析了。

processNextBroadcastLocaked前面已经分析过了,这里只需要来看finishReceiverLocked方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
 String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
 final int state = r.state;
 final ActivityInfo receiver = r.curReceiver;
 final long finishTime = SystemClock.uptimeMillis();
 final long elapsed = finishTime - r.receiverTime;
 r.state = BroadcastRecord.IDLE;
 final int curIndex = r.nextReceiver - 1;
 if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
 final Object curReceiver = r.receivers.get(curIndex);

 }
 ...

 r.receiver = null;
 r.intent.setComponent(null);
 if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
 r.curApp.mReceivers.removeCurReceiver(r);
 mService.enqueueOomAdjTargetLocked(r.curApp);
 }
 if (r.curFilter != null) {
 r.curFilter.receiverList.curBroadcast = null;
 }
 r.curFilter = null;
 r.curReceiver = null;
 r.curApp = null;
 mPendingBroadcast = null;

 r.resultCode = resultCode;
 r.resultData = resultData;
 r.resultExtras = resultExtras;
 ....
 r.curComponent = null;

 return state == BroadcastRecord.APP_RECEIVE
 || state == BroadcastRecord.CALL_DONE_RECEIVE;
}

在这里,我们最关注的代码就是17行开是的代码,从mReceivers列表中移除BroadcastRecord,并且把ReceiverListcurBroadcast设置为空,并且其他几个参数也设置为空,这样才算完成了广播的分发和处理。

总结

以上就是广播接收器的注册,以及动态、静态广播分发的分析了。关于取消注册是跟注册相关的过程,理解了注册的逻辑,取消注册也可以很快的搞清楚。关于sticky的广播,限于篇幅先不分析了。而有序广播,它在AMS端其实和静态注册的广播是差不多,不过它在调用App进程的时候是有差别的。另外关于权限相关的逻辑,以后在权限代码的分析中可以再进行关注。

看完评论一下吧

Android源码分析:再读消息循环源码

2024年10月10日 21:17

Android消息循环在应用开发中会经常涉及,我以前也分析过。不过那个时候分析的还是以很老的Android源码来进行的,并且只是分析了Java层的代码,当时的文章为:Android消息循环分析。而Native层,以及一些新增的功能,都没有涉及,今天再读源码,对其进行再次分析。

消息循环简化版本

对于应用层的开发者来说,虽然已经过了10年,java层的Api还是跟之前一样的,依然是通过Handler发送消息,Looper会中消息队列中取消息,消息会根据Handler中的callback或者消息自己的callback执,如上图所示。我之前分析的发送消息和处理消息已经比较清楚了,这块不再看了。这里主要分析一下从MessageQueue取消息,之前涉及的文件描述符的监控和Native层的一些实现等进行分析。

java层loop取消息

首先来看java层如何从消息队列取消息的,Looper中有如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static void loop() {
 final Looper me = myLooper();
 ...
 me.mInLoop = true;
 Binder.clearCallingIdentity();
 final long ident = Binder.clearCallingIdentity();
 ...
 for (;;) {
 if (!loopOnce(me, ident, thresholdOverride)) {
 return;
 }
 }
}

以上代码核心就是拿到当前线程的Looper然后,在无限循环当中取调用loopOnceloopOnce代码很长,但是忽略错误处理和Log,核心代码如下:

1
2
3
4
5
6
7
8
9
private static boolean loopOnce(final Looper me,
 final long ident, final int thresholdOverride) {
 Message msg = me.mQueue.next(); //从消息队列中取消息
 ...
 msg.target.dispatchMessage(msg); //分发消息
 ...
 msg.recycleUnchecked(); //回收消息,方便下一次发送消息使用
 return true;
}

loopOnce中主要就是去通过MessageQueue取消息,之后在分发消息,并且回收消息。再来看MessageQueuenext方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Message next() {
 final long ptr = mPtr;
 ...
 int nextPollTimeoutMillis = 0;
 for (;;) {
 nativePollOnce(ptr, nextPollTimeoutMillis);
 synchronized (this) {
 Message prevMsg = null;
 Message msg = mMessages;
 if (msg != null && msg.target == null) {
 do {
 prevMsg = msg;
 msg = msg.next;
 } while (msg != null && !msg.isAsynchronous());
 }
 if (msg != null) {
 if (now < msg.when) {
 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
 } else {
 mBlock = false;
 if (preMsg != null) {
 prevMsg.next = msg.next;
 } else {
 mMessages = msg.next;
 }
 msg.next = null;
 msg.markInUse();
 return msg;
 }
 } else {
 nextPollTimeoutMillis = -1;
 }
 ...
 }
 ...
 }
}

以上为next方法的简化,在Java层的MessageQueue的实现就是一个链表,因此向其中发送消息或者取消息的过程就是链表添加或者删除的过程。在第21行到第26行就是从链表中删除msg的过程。其中这个链表它的头节点是存放在mMessages这个变量,Message在插入链表的时候,也是按照事件先后运行放到链表当中的。

在这个方法的开头,我们看到mPtr,它就是MessageQueue在native层对应的对象,不过Native的Message和Java层的Message是相互独立的,在读取next的时候,也会通过nativePollOnce来native层来读取一个消息,另外在这里还传了一个nextPollTimeoutMillis,用来告诉native需要等待的时间,具体后面在来具体分析相关代码。

因为我们的消息循环中除了放置我们通过Handler所发送的消息之外,还会存在同步信号的屏障,比如ViewRootImpl就会在每一次scheduleTraversals的时候发送一个屏障消息。屏障消息和普通消息的区别就是没有targetHandler。因此在第10行,当我们检查到是屏障消息的时候,会跳过它, 并且查找它之后的第一条异步消息。 另外就是在这个do-while的循环条件中,我们可以看到它还有判断消息是否为Asynchronous的,我们正常创建的Handler一般async都是false,也就是说消息的这个值也是为false。而异步的,一般会被IMS,WMS,Display,动画等系统组件使用,应用开发者无法使用。

这里我们只要知道,如果有异步消息,就会先执行异步消息。在第17行,这里还会判断消息的事件,如果消息的when比当前事件大的化,那么这个消息还不能够执行,这时候需要去等待,这里就会给nextPollTimeoutMillis去赋值。

Native层的MessageQueue和Looper

我们刚刚看MessageQueue的代码时候,看到mPtr,它对应native层的MessageQueue的指针。它的初始化在MessageQueue的构造方法中,也就是调用nativeInit,其内部源码为调用NativeMessageQueue的构造方法,源码在android_os_MessageQueue.cpp中:

1
2
3
4
5
6
7
8
NativeMessageQueue::NativeMessageQueue() :
 mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
 mLooper = Looper::getForThread();
 if (mLooper == NULL) {
 mLooper = new Looper(false);
 Looper::setForThread(mLooper);
 }
}

这里我们可以看到在Native层,创建MessageQueue的时候,也会创建Looper,当然如果当前线程存在Looper则会直接使用。Native层的Looper跟Jav层一样,是存放在ThreadLocal当中的,可以看如下代码:

1
2
3
4
5
sp<Looper> Looper::getForThread() {
 int result = pthread_once(& gTLSOnce, initTLSKey);
 Looper* looper = (Looper*)pthread_getspecific(gTLSKey);
 return sp<Looper>::fromExisting(looper);
}

到这里,我们知道对于一个启动了消息循环的线程,它在Java层和Native层分别会有各自的MessageQueue和Looper,java层通过mPtr来引用Native层的对象,从而使得两层能够产生联系。

Native层pollOnce

之前分析Java层获取消息的时候,会有一个地方调用nativePollOnce,它在native拿到NativeMessageQueue之后会调用它的pollOnce方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
 mPollEnv = env;
 mPollObj = pollObj;
 mLooper->pollOnce(timeoutMillis);
 mPollObj = NULL;
 mPollEnv = NULL;

 if (mExceptionObj) {
 env->Throw(mExceptionObj);
 env->DeleteLocalRef(mExceptionObj);
 mExceptionObj = NULL;
 }
}

这里的pollObj为我们java层的MessageQueue, 这里继续调用了native层的pollOnce,代码如下:

1
2
3
4
5
6
7
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { //我们的调用流程只会传timeoutMillis
 ...
 for (;;) {
 ...
 result = pollInner(timeoutMillis);
 }
}

这里省略了一些结果处理的代码,我们可以回头在看,这可以看到开启了一个无限循环,并调用pollInner, 这个方法比较长,我们先分块看其中的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
 if (messageTimeoutMillis >= 0
 && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
 timeoutMillis = messageTimeoutMillis;
 }
}
int result = POLL_WAKE;
mResponses.clear(); //清除reponses列表和计数
mResponseIndex = 0;
mPolling = true;

struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

mPolling = false;

这里timeoutMillis是我们从java层传过来的下一个消息的执行事件,而mNextMessageUptime是native层的最近一个消息的执行事件,这个根据这两个字段判断需要等待的事件。

在之后调用epoll_wait来等待I/O事件,或者到设置的超时时间结束等待,这样做可以避免Java层和Native层的循环空转。此处的epoll_wait除了避免循环空转还有另一个作用,我们之前在分析IMS也使用过LooperaddFd,这里如果对应的文件描述符有变化,这里就会拿到,并反应在eventCount上,这里我们先不具体分析,后面再看。

Native消息的读取和处理

当等待完成之后,就会去native的消息队列中取消息和处理,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Done: ;
 mNextMessageUptime = LLONG_MAX;
 while (mMessageEnvelopes.size() != 0) {
 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
 if (messageEnvelope.uptime <= now) {
 {
 sp<MessageHandler> handler = messageEnvelope.handler;
 Message message = messageEnvelope.message;
 mMessageEnvelopes.removeAt(0);
 mSendingMessage = true;
 mLock.unlock();
 handler->handleMessage(message);
 }

 mLock.lock();
 mSendingMessage = false;
 result = POLL_CALLBACK;
 } else {
 mNextMessageUptime = messageEnvelope.uptime;
 break;
 }
 }

在Native中消息是放在mMessageEnvelope当中,这是一个verctor也就是一个动态大小的数组。不过不看这个的化,我们可以看到这里读取消息,以及读取它的执行时间uptime跟java层的代码是很像是的,甚至比java层还要简单许多,就是直接拿数组的第一条。之后使用MessageHandler执行handleMessage。这里的MessageHandler跟java层的也是很像,这里再列一下MessageEnvelopeMessage的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
struct MessageEnvelope {
 MessageEnvelope() : uptime(0) { }

 MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
 : uptime(u), handler(std::move(h)), message(m) {}

 nsecs_t uptime;
 sp<MessageHandler> handler;
 Message message;
};

struct Message {
 Message() : what(0) { }
 Message(int w) : what(w) { }

 /* The message type. (interpretation is left up to the handler) */
 int what;
};

这里和java层的区别是,拆分成了两个结构体,但是呢比java层的还是要简单很多。到这里Native层和Java层对应的消息循环体系就分析完了。但是Native层除了这个消息循环还有一些其他东西,就是前面说到的文件描述符的消息传递。

文件描述符消息读取和处理

前面在pollOnce中还是有关于文件描述符消息的处理,这里继续分析。前面的epoll_wait就会读取相关的事件,读取完事件之后的处理如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
if (eventCount < 0) { //如果读出来的eventCount小于0,则说明有错误
 if (errno == EINTR) { //处理错误,并且跳转到Done去读取native层的消息
 goto Done;
 }
 result = POLL_ERROR;
 goto Done;
}

if (eventCount == 0) { //直接超时,没有读到事件
 result = POLL_TIMEOUT;
 goto Done;
}

for (int i = 0; i < eventCount; i++) { //根据返回的条数,来处理消息
 const SequenceNumber seq = eventItems[i].data.u64;
 uint32_t epollEvents = eventItems[i].events;
 if (seq == WAKE_EVENT_FD_SEQ) { //序列为这个序列被定义成为唤醒事件
 if (epollEvents & EPOLLIN) {
 awoken();
 } else {
 }
 } else {
 const auto& request_it = mRequests.find(seq);
 if (request_it != mRequests.end()) {
 const auto& request = request_it->second;
 int events = 0;
 if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
 if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
 if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
 if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
 mResponses.push({.seq = seq, .events = events, .request = request});
 } else {
 ...
 }
 }
}

前面的错误处理我们直接看我的注释即可。后面会根据返回的eventCount来一次对每一个eventItem做处理,其他它的u64为序列号,这些为注册到LoopermRequests的序列号,其中1为WAKE_EVENT_FD_SEQ,也就是mWakeEventFd的序列,这里唤醒我们先不管了,直接看后面的正常的文件描述符事件监听。 这里首先会通过seq找到对应的Request,并根据epollEvents来设置他们的事件类型,之后封装成为Response放到mResponses当中。在这些做完,后面同样是跳转到Done后面的代码块,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Done: ;
 ...
 for (size_t i = 0; i < mResponses.size(); i++) {
 Response& response = mResponses.editItemAt(i);
 if (response.request.ident == POLL_CALLBACK) {
 int fd = response.request.fd;
 int events = response.events;
 void* data = response.request.data;
 int callbackResult = response.request.callback->handleEvent(fd, events, data);
 if (callbackResult == 0) {
 AutoMutex _l(mLock);
 removeSequenceNumberLocked(response.seq);
 }

 response.request.callback.clear(); //移除response对与callback的引用
 result = POLL_CALLBACK;
 }
 }

这里则是遍历刚刚我们填充的mResponses数组,从其中取出每一个Response,并调用它的Request的Callback回调的handleEvent方法,它的使用我们之前分析IMSServiceManager启动的时候已经见到过了。

以上说的是Java层会初始化Handler和Looper的情况,如果只是Native层使用的话,一般怎么用的呢。我们以BootAnimation中的使用为例,它是在BootAnimation.cpp当中,在初始化BootAnimation对象的时候,会创建一个Looper,代码如下:

1
new Looper(false)

readyToRun中添加文件描述符的监听:

1
2
3
4
5
status_t BootAnimation::readyToRun() {
 ...
 mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
 new DisplayEventCallback(this), nullptr);
}

最后去循环调用pollOnce,来获取消息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bool BootAnimation::android() {
 do {
 processDisplayEvents();
 ...
 } while (!exitPending());
}

void BootAnimation::processDisplayEvents() {
 mLooper->pollOnce(0);
}

这就是Android Framework当中,大部分的Native场景使用消息循环的方式。而Native中,想要跟Java层一样发送消息,则是调用Looper的sendMessage方法。而Native层的Handler我们可以理解为只是一个Message的回调,和java层的Handler功能不可同日而语。

异步消息

在Java层的消息循环中,消息是有同步和异步之分的,异步消息一般都会伴随则屏障消息,我们之前分析的获取next消息中可以看到,如果第一个消息是屏障消息,会找后面的第一条异步消息来执行。

同时在enqueueMessage的代码中也有如下逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//MessageQueue.java
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
 prev = p;
 p = p.next;
 if (p == null || when < p.when) {
 break;
 }
 if (needWake && p.isAsynchronous()) {
 needWake = false;
 }
}
msg.next = p;
prev.next = msg;

插入异步消息会改变唤醒等待的状态,如果链表头是屏障消息,且之前调用next的时候mBlocked设置为了true,且当前是异步消息会设置成唤醒,但是如果当前的消息队列中已经有了比当前消息更早执行的消息,则不会唤醒。

到这就完成了消息循环的所有分析了。也欢迎读者朋友交流探讨。

看完评论一下吧

Android源码分析:系统进程中事件的读取与分发

2024年9月26日 13:44

之前分析的是从InputChannel中读取Event,并且向后传递,进行消费和处理的过程。但是之前的部分呢,事件如何产生,事件怎么进入到InputChanel当中的,事件又是如何跨进程到达App进程,这里继续来分析。

以上为system进程的流程的简化图,这里我们可以看到几个重要的组件,这里以触摸事件来进行分析(后文的分析也将会以触摸事件为主进行分析)并且简单的描绘了事件从EventHub到服务端的InputChannel发送事件的全部过程。具体内容一起来看下面的代码。

InputManagerService的创建

因为事件的分发涉及到不少类,我们先从InputManagerService(IMS)的初始化出发,进行分析。入口代码在SystemServer.java中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
WindowManagerService wm = null;
InputManagerService inputManager = null;
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
 new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
 /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);

inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
inputManager.start();
}

这里我们可以看到WMS的创建我们传入了IMS,并且IMS也依赖WindowMnagerCallbacks,我们先看一下IMS的构造方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public InputManagerService(Context context) {
 this(new Injector(context, DisplayThread.get().getLooper()));
}

@VisibleForTesting
InputManagerService(Injector injector) {
 ...
 mHandler = new InputManagerHandler(injector.getLooper());
 mNative = injector.getNativeService(this);
 ...
}

我们主要关注这个mNative的构建,它是NativeImpl,它的创建过程如下:

1
new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());

这里的Looper是前面传进来的DisplayThread的Looper。在NativeImpl的构造方法中调用了init方法,并获取到了它的native指针,这里需要看com_android_server_input_InputManagerService.cpp中的natvieInit方法,代码如下:

1
2
3
4
5
6
7
8
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
 jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
 NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
 messageQueue->getLooper());
 im->incStrong(0);
 return reinterpret_cast<jlong>(im);
}

这里创建了NativeInputManager

NativeInputManager初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
NativeInputManager::NativeInputManager(jobject contextObj,
 jobject serviceObj, const sp<Looper>& looper) :
 mLooper(looper), mInteractive(true) {
 JNIEnv* env = jniEnv();

 mServiceObj = env->NewGlobalRef(serviceObj);

 {
 AutoMutex _l(mLock);
 mLocked.systemUiLightsOut = false;
 mLocked.pointerSpeed = 0;
 mLocked.pointerAcceleration = android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION;
 mLocked.pointerGesturesEnabled = true;
 mLocked.showTouches = false;
 mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
 }
 mInteractive = true;

 InputManager* im = new InputManager(this, this);
 mInputManager = im;
 defaultServiceManager()->addService(String16("inputflinger"), im);
}

这个构造方法中,传入的jobject为我们之前的NativeImpl,后面有需要调用java层的时候会用到它。除此之外我们看到又创建了一个InputManger,并且把它注册到了ServiceManger当中,名称为inputflinger

我们继续看InputManager的初始化代码,它传如的两个参数readerPolicydispatcherPolicy的实现都在NativeInputManager当中。它的代码如下:

1
2
3
4
5
6
7
8
InputManager::InputManager(
 const sp<InputReaderPolicyInterface>& readerPolicy,
 const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
 mDispatcher = createInputDispatcher(dispatcherPolicy);
 mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
 mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);
 mReader = createInputReader(readerPolicy, *mBlocker);
}

这里首先创建了InputDispatcher,之后创建的mClassifiermBlockerInputDispatcher一样都是继承自InputListenerInterface,它们的作用为在事件经过InputDispatcher分发之前,可以做一些预处理。最后创建InputReader,事件会经由它传递到InputDispatcher,最后再由InputDispatcher分到到InputChannel。下面来详细分析。

事件源的初始化

因为InputDispatcher初始化代码比较简单,我们从createInputReader的源码开始看起来:

1
2
3
4
std::unique_ptr<InputReaderInterface> createInputReader(
 const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
 return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
}

我们可以看到在创建InputReader之前首先创建了一个EventHub,看名字我们就知道它是一个事件的收集中心。我们看它的构造方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
EventHub::EventHub(void)
 : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
 mNextDeviceId(1),
 ...
 mPendingINotify(false) {
 ensureProcessCanBlockSuspend();

 mEpollFd = epoll_create1(EPOLL_CLOEXEC); //创建epoll实例,flag表示执行新的exec时候会自动关闭

 mINotifyFd = inotify_init1(IN_CLOEXEC); //创建inotify实例,该实例用于监听文件的变化

 if (std::filesystem::exists(DEVICE_INPUT_PATH, errorCode)) {
 addDeviceInputInotify();
 } else {
 addDeviceInotify();
 isDeviceInotifyAdded = true;

 }

 struct epoll_event eventItem = {};
 eventItem.events = EPOLLIN | EPOLLWAKEUP;
 eventItem.data.fd = mINotifyFd;
 int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
 int wakeFds[2];
 result = pipe2(wakeFds, O_CLOEXEC);

 mWakeReadPipeFd = wakeFds[0];
 mWakeWritePipeFd = wakeFds[1];

 result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);

 result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

 eventItem.data.fd = mWakeReadPipeFd;
 result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}

从上面的代码我们可以看到这里主要为创建inotify并且通过epoll去监听文件的变化,其中还是用管道创建了wakeReadPipewakeReadPipe的文件描述,用于接收回调。我们先看一下addDeviceInputInotify()方法:

1
2
3
4
void EventHub::addDeviceInputInotify() {
 mDeviceInputWd = inotify_add_watch(mINotifyFd, DEVICE_INPUT_PATH, IN_DELETE | IN_CREATE);

}

其中DEVICE_INPUT_PATH的值为/dev/input,也就是说把这个path放到mINofiyFd的监控当中。对于了解Linux的人应该知道,在Linux中万物结尾文件,因此我们的输入也是文件,当事件发生的时候便会写入到/dev/input下面,文件变化我们也会得到通知。我这里使用ls命令打印了一下我的手机,/dev/input下面有如下文件:

1
event0 event1 event2 event3 event4 event5

具体这些文件的写入,那就是内核和驱动相关的东西了,我们这里不再讨论。而事件的读取,我们后面再进行分析。

IMS的启动

各个对象都构建完成之后,IMS要进行启动,才能够对事件进行处理并且分发。SystemServer中已经调用了IMS的start方法,它其中又会调用NativeInputMangerstart方法,最终会调用 native层的InputManagerstart方法。而其中分别又调用了Dispatcher的start方法和Reader的start方法。我们分别分析。

InputDispater 调用start

1
2
3
4
5
6
7
8
status_t InputDispatcher::start() {
 if (mThread) {
 return ALREADY_EXISTS;
 }
 mThread = std::make_unique<InputThread>(
 "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
 return OK;
}

这个方法中主要创建了InputThread,并且给它传了两个lambda,分别执行InputDispatchdispatchOnce方法和执行Looper的wake方法。我们看InputThread的构造方法:

1
2
3
4
5
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
 : mName(name), mThreadWake(wake) {
 mThread = new InputThreadImpl(loop);
 mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

可以看到其中创建了InputThreadImpl,这个类才是真的继承的系统的Thread类,这里构建完成它就继续调用了它的run方法,这样它就会启动了。这里我们需要注意这个 线程的优先级,为PRIORITY_URGEN_DISPLAY,可以看到优先级是非常高了。

1
2
3
4
bool threadLoop() override {
 mThreadLoop();
 return true;
}

另外就是我们传进来的loop传入了这个对象,并且在它的threadLoop中会执行它。对于native中的线程,我们在threadLoop中实现逻辑就可以了,并且这里我们返回值为true,它会继续循环执行 。而我们传入的另一个lambda,则是在线程推出的时候调用。这个线程循环中执行的就是我们的InputDispatch中 的dispatchOnce方法,也就是消息的投递,后面再来分析。

InputReader调用start方法

1
2
3
4
5
6
7
8
status_t InputReader::start() {
 if (mThread) {
 return ALREADY_EXISTS;
 }
 mThread = std::make_unique<InputThread>(
 "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
 return OK;
}

这里的初始化,我们可以看到跟前面的InputDispatch很类似,连InputThread用的都是同一个类,内部也就一样有InputThreadImpl了。这里则是调用了InputReader内部的loopOnce方法。到这里系统就完成了输入事件分发的初始化了。

我们在看事件的分发之前,先看一下应用中的接收和系统的InputDispatch进行连接的过程。

InputChannel的注册

我们之前分析应用层的事件传递的时候,只是谈到了InputChannel是在WMS调用如下代码生成的:

1
mInputChannel = mWmService.mInputManager.createInputChannel(name);

但是内部如何创建InputChannel的,以及 这个InputChannel是如何收到消息的我们都没有涉及,我们现在继续分析它一下。这个createInputChannel内部最终会调用到native层的InputDispatchercreateInputChannel方法, 代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
 std::unique_ptr<InputChannel> serverChannel;
 std::unique_ptr<InputChannel> clientChannel;
 status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 ...
 { // acquire lock
 std::scoped_lock _l(mLock);
 const sp<IBinder>& token = serverChannel->getConnectionToken();
 int fd = serverChannel->getFd();
 sp<Connection> connection =
 n1ew Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
 ...
 mConnectionsByToken.emplace(token, connection);

 std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token);

 mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
 } // release lock

 // Wake the looper because some connections have changed.
 mLooper->wake();
 return clientChannel;
}

首先是第4行代码,这里创建了InputChannel,而它又分为serverChannelclientChannel,返回调用方的是`clientChannel。

我们先进去看看其源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
status_t InputChannel::openInputChannelPair(const std::string& name,
 std::unique_ptr<InputChannel>& outServerChannel,
 std::unique_ptr<InputChannel>& outClientChannel) {
 int sockets[2];
 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { //创建socket对
 ..
 return result;
 }
 ..
 sp<IBinder> token = new BBinder();

 std::string serverChannelName = name + " (server)";
 android::base::unique_fd serverFd(sockets[0]); //获取server socket fd
 outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token); //创建server InputChannel

 std::string clientChannelName = name + " (client)";
 android::base::unique_fd clientFd(sockets[1]);
 outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token); //创建Client InputChannel
 return OK;
}

以上代码我们可以看到就是创建了一对socket,分别放到两个InputChannel当中,并且这里创建了一个BBinder作为两个InputChannel的token,具体用处我们后面会再提到。此时可以继续回看前面的createInputChannel方法,在11行,创建了一个 Connection对象,并且以前面创建的BBinder为key放到了mConnectionsByToken当中,Connection的用处留到后面继续讲。

在15行创建了一个callback,其中会执行InputDispatcherhandleReceiveCallback方法,并且这个callback被添加looper的addFd的时候设置进去了,这里的fd就是之前创建的ServerInputChannel的socket的文件描述符。到这里就完成了初始化,添加了服务端InputChannel的文件描述符监听。

事件触发

我们之前在分析InputManger的启动的时候,已经看到了事件是通过/dev/input来通知到EventHub,而InputReader通过Looper监听了/dev/input的文件描述符,从而让我们事件传递的系统动起来。那么我们首先就从InputReaderloopOnce开始看起来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void InputReader::loopOnce() {
 ...
 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

 { // acquire lock
 std::scoped_lock _l(mLock);
 mReaderIsAliveCondition.notify_all();

 if (count) {
 processEventsLocked(mEventBuffer, count);
 }
 ...
 } // release lock
 ...
 mQueuedListener.flush();
}

我们这里省略了设备变化,超时等相关的代码,仅仅保留了事件读取相关的部分。我们看到,首先在第3行中,从EventHub中去获取新的事件,之后在第10行,去处理这些事件,第15行会清楚所有的事件,我们分别看看各个里面的逻辑。

从EventHub读取事件

首先是getEvents方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
 std::scoped_lock _l(mLock);
 struct input_event readBuffer[bufferSize];
 RawEvent* event = buffer;
 size_t capacity = bufferSize;
 bool awoken = false;
 for (;;) {
 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

 bool deviceChanged = false;
 //pendingIndex小于PendingCount,说明之前有事件还为处理完
 while (mPendingEventIndex < mPendingEventCount) {
 const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
 ...
 Device* device = getDeviceByFdLocked(eventItem.data.fd);
 if (device == nullptr) { //未能找到device,报错跳出
 continue;
 }

 // EPOLLIN表示有事件可以处理
 if (eventItem.events & EPOLLIN) {
 int32_t readSize =
 read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
 // 接收到通知之前,设备以及不见了
 deviceChanged = true;
 closeDeviceLocked(*device);
 } else if() { //其中的错误情况,忽略掉
 } else {
 int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

 size_t count = size_t(readSize) / sizeof(struct input_event); //根据一个事件的大小,来算同一个设备上面读取到的事件的个数
 //以下为具体保存事件到event当中
 for (size_t i = 0; i < count; i++) {
 struct input_event& iev = readBuffer[i];
 event->when = processEventTimestamp(iev);
 event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
 event->deviceId = deviceId;
 event->type = iev.type;
 event->code = iev.code;
 event->value = iev.value;
 event += 1;
 capacity -= 1;
 }
 if (capacity == 0) { //缓冲区已经满了,无法在记录事件,跳出
 mPendingEventIndex -= 1;
 break;
 }
 }
 } else if (eventItem.events & EPOLLHUP) {
 ...
 } else {
 ...
 }
 }
 ...
 //event和buffer地址不同说明已经拿到事件了,可以跳出循环
 if (event != buffer || awoken) {
 break;
 }

 mPendingEventIndex = 0;
 mLock.unlock(); // poll之前先加锁
 int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 mLock.lock(); // poll完之后从新加锁

 if (pollResult == 0) {
 // Timed out.
 mPendingEventCount = 0;
 break;
 }

 if (pollResult < 0) {
 mPendingEventCount = 0;
 if (errno != EINTR) {
 usleep(100000);
 }
 } else {
 mPendingEventCount = size_t(pollResult);
 }
 }

 // event为填充之后的指针地址,而buffer为开始的地址,相减获得count
 return event - buffer;
}

这个方法是很复杂的,但是我们主要分析事件的分发,因此其中关于设备变化,设备响应,错误处理等等相关的代码都省略了。这个方法,我们传入了一个RawEvent的指针用来接收事件,另外传了bufferSize来表示我们所能接收的事件数量。这个方法使用了两层循环来进行逻辑的处理,外层的为无限循环。当我们第一次进入这个方法当中,mPendingEventCountmPendingEventIndex都是0,因此不会进入第二层的循环,这个时候会执行到64行,调用epoll_wait系统调用,去读取事件,读取的结果会放到mPendingEventItems当中,之后会算出pendingCount。这样继续循环,我们就可以进入内存循环当中了。 在刚刚的PendingEventItem中并没有存储具体的事件,而是存储的事件发生的设备文件描述符,在内存的循环中,首先会根据设备的描述符查找设备,并对其进行检查。之后再从设备当中读取事件,拼装成为需要向后分发的事件。 这里的count有点让人迷糊,我画了个图如下所示:

其中我们真正读取的事件的数量,是要看有几个设备,每个设备有多少个事件,对其进行计算。 到这里我们就获取到了事件,这里可以回到InputReader中继续往下看了。

InputReader对事件进行处理

在这里的处理调用的是processEventsLocked,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
 for (const RawEvent* rawEvent = rawEvents; count;) {
 int32_t type = rawEvent->type;
 size_t batchSize = 1;
 //如果不是设备处理相关的事件,则执行。
 if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
 int32_t deviceId = rawEvent->deviceId;
 while (batchSize < count) {
 if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
 rawEvent[batchSize].deviceId != deviceId) {
 //当遇到设备整删除事件,或者不是当前设备的事件,就不能进行批量处理,跳过。
 break;
 }
 batchSize += 1;
 }
 processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
 } else {
 //设备添加删除之类的事件处理,跳过
 }
 count -= batchSize;
 rawEvent += batchSize;
 }
}

这个方法中主要是对与设备增加删除事件和普通事件进行分别处理,如果是普通的事件,会对同一个设备上的事件进行批量处理,批量处理则会调用processEventsForDeviceLocked方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
 size_t count) {
 auto deviceIt = mDevices.find(eventHubId);
 if (deviceIt == mDevices.end()) {
 //没有找到设备,返回
 return;
 }

 std::shared_ptr<InputDevice>& device = deviceIt->second;
 if (device->isIgnored()) { //是被忽略的设备,跳过
 return;
 }

 device->process(rawEvents, count);
}

这个方法中主要是查找设备,找到未忽略的设备则会调用设备的process方法进行处理。 InputDevice只是设备的抽象,而其中的处理又会调用InputMapper的方法,InputMapper是抽象类,它有许多的实现,比如我们的触摸事件就会有TouchuInputMapperMultiTouchInputMapper,各种不同的InputMapper会对事件进行处理,拼装成符合相关类型的事件,其中逻辑我们就不继续进行追踪了。

对于touch事件,这个process处理完成,在TouchInputMapper中最终会调用dispatchMotion,这个方法代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void TouchInputMapper::dispatchMotion(...) {
 PointerCoords pointerCoords[MAX_POINTERS];
 PointerProperties pointerProperties[MAX_POINTERS];
 uint32_t pointerCount = 0;
 ...
 const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
 const int32_t deviceId = getDeviceId();
 std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
 std::for_each(frames.begin(), frames.end(),
 [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
 NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
 policyFlags, action, actionButton, flags, metaState, buttonState,
 MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
 pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
 downTime, std::move(frames));
 getListener().notifyMotion(&args);
}

其中有许多关于多点触控,事件处理的判断,这里只关注最后的部分,就是将事件组装成一个NotifyMotionArgs对象,并调用ListenernotifyMotion方法。这里的getListener()内部首先会调用getContenxt获取Context,而这个Context就是InputReader的内部成员mContext,这这个Listener也就是我们之前在初始化InputReader时候它的成员变量mQueuedListener,那我们下面继续去看它的notifyMotion

notifyMotion

1
2
3
4
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
 traceEvent(__func__, args->id);
 mArgsQueue.emplace_back(std::make_unique<NotifyMotionArgs>(*args));
}

这里是直接把之前的那个变量放到mArgsQueue当中了。这个时候,我们需要留意一下之前InputReadeloopOnce的15行,这里调用的 flush方法,也是这个QueuedInputListener内部的:

1
2
3
4
5
6
void QueuedInputListener::flush() {
 for (const std::unique_ptr<NotifyArgs>& args : mArgsQueue) {
 args->notify(mInnerListener);
 }
 mArgsQueue.clear();
}

这里这是掉用了我们传进来的NotifyArgs的notify方法,并且传过来的参数mInnerListener是我们之前创建InputManager时候创建的,这里会有三层嵌套,首先是UnWantedInteractionBlocker先处理,之后它会按情况传递给InputClassifier处理,最后是在InputDispatcher当中处理。

我们先看看看notify当中做了什么,再继续往后看。

1
2
3
void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
 listener.notifyMotion(this);
}

这里也是比较简单,就是直接调用了linster的notifyMotion方法,我们可以直接去看了。因为我们主要关注 传递,而不关注处理,这里就跳过,直接看InputDispatcher中的这个方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
 if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
 args->pointerProperties)) {
 return; //不合法的触摸事件直接返回
 }

 uint32_t policyFlags = args->policyFlags;
 policyFlags |= POLICY_FLAG_TRUSTED;

 android::base::Timer t;

 bool needWake = false;
 { // acquire lock
 mLock.lock();
 ...
 std::unique_ptr<MotionEntry> newEntry =
 std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
 args->source, args->displayId, policyFlags,
 args->action, args->actionButton, args->flags,
 args->metaState, args->buttonState,
 args->classification, args->edgeFlags,
 args->xPrecision, args->yPrecision,
 args->xCursorPosition, args->yCursorPosition,
 args->downTime, args->pointerCount,
 args->pointerProperties, args->pointerCoords);
 ...
 needWake = enqueueInboundEventLocked(std::move(newEntry));
 mLock.unlock();
 } // release lock

 if (needWake) {
 mLooper->wake();
 }
}

在这里则是执行完一些检查之后,把事件封装成为MotionEntry,调用enqueueInboundEventLocked,最后调用looperwake方法。enqueueInboundEventLocked代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
 bool needWake = mInboundQueue.empty();
 mInboundQueue.push_back(std::move(newEntry));
 EventEntry& entry = *(mInboundQueue.back());
 switch (entry.type) {
 case EventEntry::Type::MOTION: {
 if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { //返回true的时候,事件会被移除不处理
 mNextUnblockedEvent = mInboundQueue.back();
 needWake = true;
 }
 break;
 }
 ...
 }

 return needWake;
}

在这里,首先把事件放入mInboundQueue这个deque当中,最后根据事件的类型和信息要不要唤醒looper,如果事件不被移除needWake就为false,前面的wake也不会被调用。但是这个是否调用,不影响我们的后续分析,因为InputDispatch中的Thead会一直循环调用。

InputDispatcher分发消息

说到这里,我们就该来看看InputDispatcherdispatchOnce方法了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void InputDispatcher::dispatchOnce() {
 nsecs_t nextWakeupTime = LONG_LONG_MAX;
 { // acquire lock
 std::scoped_lock _l(mLock);
 mDispatcherIsAlive.notify_all();

 if (!haveCommandsLocked()) {
 dispatchOnceInnerLocked(&nextWakeupTime);
 }

 if (runCommandsLockedInterruptable()) {
 nextWakeupTime = LONG_LONG_MIN;
 }
 ...
 } // release lock

 //等待下一次调用
 mLooper->pollOnce(timeoutMillis);
}

这里有不少处理下一次唤醒的逻辑,我们都跳过,主要就看一下第8行,进行这一次的实际执行内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
 ...
 if (!mPendingEvent) { //当前没有待处理的pending事件
 if (mInboundQueue.empty()) { //如果事件列表为空
 ...
 if (!mPendingEvent) {
 return;
 }
 } else {
 // 列表中拿一个事件
 mPendingEvent = mInboundQueue.front();
 mInboundQueue.pop_front();
 traceInboundQueueLengthLocked();
 }
 ...
 }

 bool done = false;
 ..
 switch (mPendingEvent->type) {
 ...
 case EventEntry::Type::MOTION: {
 std::shared_ptr<MotionEntry> motionEntry =
 std::static_pointer_cast<MotionEntry>(mPendingEvent);
 ...
 done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
 break;
 }
 ...
 }

 if (done) {
 ...
 releasePendingEventLocked();
 *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
 }
}

我们将这个方法进行了简化,仅仅保留了触摸事件的部分代码。首先判断mPendingEvent是否为空,为空的时候我们需要到mPendingEvent中去拿一个,我们之前插入的是尾部,这里是从头部取的。拿到事件进行完种种处理和判断之后,会调用dispatchMotionLocked进行触摸事件的分发:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
 DropReason* dropReason, nsecs_t* nextWakeupTime) {
 if (!entry->dispatchInProgress) { //设置事件正在处理中
 entry->dispatchInProgress = true;

 }

 if (*dropReason != DropReason::NOT_DROPPED) {
 //对于要抛弃的事件这里进行处理,返回
 return true;
 }

 const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER); //读取是否为POINTER
 std::vector<InputTarget> inputTargets;

 bool conflictingPointerActions = false;
 InputEventInjectionResult injectionResult;
 if (isPointerEvent) {
 //如果屏幕触摸事件则去找到对应的window
 injectionResult =
 findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
 &conflictingPointerActions);
 } else {
 // Non touch event. (eg. trackball)
 injectionResult =
 findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
 }
 if (injectionResult == InputEventInjectionResult::PENDING) {
 return false;
 }

 setInjectionResult(*entry, injectionResult);
 ...

 // Dispatch the motion.
 dispatchEventLocked(currentTime, entry, inputTargets);
 return true;
}

这个方法中依然是对于事件做很多的处理和判断,比如否要抛弃等。但是其中最终要的是调用findFocusedWIndowTargetsLocked来找到我们的事件所对应的Window,并且保存相关信息到inputTargets当中,这里获取inputTargets的过程比较复杂,但是简单来说呢就是从之前我们保存在InputDispatcher中的mConnectionsByToken中查找到对应的条目,这里暂不深入分析。拿到这个之后就是调用dispatchEventLocked去分发,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
 std::shared_ptr<EventEntry> eventEntry,
 const std::vector<InputTarget>& inputTargets) {
 ...
 for (const InputTarget& inputTarget : inputTargets) {
 sp<Connection> connection =
 getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
 if (connection != nullptr) {
 prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
 } else {

 }
 }
}

通过这里我们可以看到首先是通过inputTarget去拿到connectionToken,再通过它拿到Connection。最后通过调用prepareDispatchCycleLocked

1
2
3
4
5
6
7
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
 const sp<Connection>& connection,
 std::shared_ptr<EventEntry> eventEntry,
 const InputTarget& inputTarget) {
 ...
 enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

这个方法简化的化,这是调用第6行的这个方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
 const sp<Connection>& connection,
 std::shared_ptr<EventEntry> eventEntry,
 const InputTarget& inputTarget) {

 bool wasEmpty = connection->outboundQueue.empty();

 // Enqueue dispatch entries for the requested modes.
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_IS);
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);

 // If the outbound queue was previously empty, start the dispatch cycle going.
 if (wasEmpty && !connection->outboundQueue.empty()) {
 startDispatchCycleLocked(currentTime, connection);
 }
}

其中对于消息会尝试按照每一种mode都调用enqueueDIspatchEntryLocked方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
 std::shared_ptr<EventEntry> eventEntry,
 const InputTarget& inputTarget,
 int32_t dispatchMode) {

 int32_t inputTargetFlags = inputTarget.flags;
 if (!(inputTargetFlags & dispatchMode)) {
 return;
 }
 inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;

 std::unique_ptr<DispatchEntry> dispatchEntry =
 createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);

 EventEntry& newEntry = *(dispatchEntry->eventEntry);
 // Apply target flags and update the connection's input state.
 switch (newEntry.type) {
 case EventEntry::Type::MOTION: {
 const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
 constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
 static_cast<int32_t>(IdGenerator::Source::OTHER);
 dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
 ...


 if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
 (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
 break;
 }

 dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
 inputTarget.inputChannel->getConnectionToken());
 break;
 }
 ...
 }
 connection->outboundQueue.push_back(dispatchEntry.release());
 traceOutboundQueueLength(*connection);
}

在这个方法中,又把事件封装成为dispatchEntry,并放到Connection内部的outboundQueue这个队列当中。

到这里我们可以回看上面的enqueueDispatchEntriesLocked的最后一块代码,那里有判断了如果这个outboundQueue队列不为空,则会执行最后的startDispatchCycleLocked,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
 const sp<Connection>& connection) {

 while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
 DispatchEntry* dispatchEntry = connection->outboundQueue.front();
 dispatchEntry->deliveryTime = currentTime;
 ...
 // Publish the event.
 status_t status;
 const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
 switch (eventEntry.type) {
 ...
 case EventEntry::Type::MOTION: {
 const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
 ...

 std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);

 status = connection->inputPublisher
 .publishMotionEvent(dispatchEntry->seq,
 dispatchEntry->resolvedEventId,
 motionEntry.deviceId, motionEntry.source,
 motionEntry.displayId, std::move(hmac),
 dispatchEntry->resolvedAction,
 motionEntry.actionButton,
 dispatchEntry->resolvedFlags,
 motionEntry.edgeFlags, motionEntry.metaState,
 motionEntry.buttonState,
 motionEntry.classification,
 dispatchEntry->transform,
 motionEntry.xPrecision, motionEntry.yPrecision,
 motionEntry.xCursorPosition,
 motionEntry.yCursorPosition,
 dispatchEntry->rawTransform,
 motionEntry.downTime, motionEntry.eventTime,
 motionEntry.pointerCount,
 motionEntry.pointerProperties, usingCoords);
 break;
 }
 }
 ...

 }
}

在这个方法中,则是从outboundQueue把所有的事件一条一条的取出来,解包成它要的类型,比如触摸事件就是MotionEntry,经过判断和一些处理之后,调用connection中的inputPublisherpublishMotionEvent方法,这里的inputPublisher我们之前分析创建InputChannel的时候有所了解,创建它所传的InputChannel为Server端的那个。 我们这里看一下它的publishMotionEvent方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
status_t InputPublisher::publishMotionEvent(...) {

 InputMessage msg;
 msg.header.type = InputMessage::Type::MOTION;
 msg.header.seq = seq;
 msg.body.motion.eventId = eventId;
 ...
 msg.body.motion.pointerCount = pointerCount;
 for (uint32_t i = 0; i < pointerCount; i++) {
 msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
 msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
 }

 return mChannel->sendMessage(&msg);
}

这里主要创建了InputMessage,将之前MotionEvent的所有参数放进去,通过ServerInputChannel调用sendMessage发送出去,sendMessage的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
status_t InputChannel::sendMessage(const InputMessage* msg) {
 const size_t msgLength = msg->size();
 InputMessage cleanMsg;
 msg->getSanitizedCopy(&cleanMsg);
 ssize_t nWrite;
 do {
 nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
 } while (nWrite == -1 && errno == EINTR);

 return OK;
}

我们知道,InputChannel内部的文件描述符为socket的标记,这里调用send方法,也就是通过socket把信息发送出去,这样的话,我们Client端的的socket也就会接收到,通过Looper,Client端的EventListener就可以接收到消息,我们的应用便可以接收到,到这里便把事件分发,完整的串起来了。

总结

以上就是事件在系统进程中的处理,包括它的事件获取,事件处理,最后通过socket发送,这样我们在客户端进程的InputChannel就能够接收到通知,客户端能够处理事件。配合我们之前分析过应用层的事件分发,到这里,不算事件的驱动相关的部分,事件分发的整个流程我们都有所了解了。

在这里事件从系统system_server通过Server的InputChannel发送的客户端的InputChannel,所采用的是unix的socket功能,而不是使用的binder或者其他的跨进程服务。这一块,结合我在网上查找的资料,以及我自己的想法,我想这里这样做的原因是,unix的sockt pair使用上很简单,并且运行效率很高效,不需要像binder一样涉及到进程和线程的切换。另外就是socket使用了fd,目标进程可以直接监听到事件的来临,而不是向binder一样需要有相应的接口涉及,可以更加实时的接收到事件,也不会因为binder线程阻塞而卡顿。

当然这是我的一些想法,也欢迎读者朋友说说你对于这块的想法。

看完评论一下吧

Android源码分析:从源头分析View事件的传递

2024年9月20日 17:20

对于应用开发者的我们来说,经常会处理按钮点击,键盘输入等事件,而我们的处理一般都是在Activity中或者View中去做的。我们在上一篇文章中分析了View和Activity与Window的关系,其中的ViewRootImpl和我们的事件传递息息相关,上文未能分析,本文将对其进行分析。

事件介绍

事件是什么呢,广义上事件的发生可能在软件也可能在硬件层,在Android设备当中,我们会有可能有键盘触发,触摸触发,鼠标触发的各种事件。我们关注的通常有两种事件: 按键事件(KeyEvent): 这种色包括物理的按键,Home键,音量键,也包括软键盘触发的事件。 触摸事件(TouchEvent): 手指在屏幕上触摸触发的事件,可能是点击,也可能是拖动。

对于按键事件,一般有ACTION_DOWNACTION_UP两种状态,对于KeyEvent所支持的所有keyCode,我们都可以在KeyEvent当中找到。

而对于触摸事件来说,除了DOWNUP两种状态之外,还有ACTION_MOVEACTION_CANCEL等状态。

应用层的事件类图如下图所示:

classDiagram
class Parcelable {
<<interface>>
}
class InputEvent {
<<abstract>>
}
class KeyEvent
class MotionEvent
InputEvent<|--KeyEvent
InputEvent<|--MotionEvent
Parcelable<|..KeyEvent
Parcelable<|..MotionEvent
Parcelable<|..InputEvent

事件传递到View

我们一般处理View的onClick事件,而这个事件是在View的onTouchEvent中进行处理并执行的,在View中我们可以向上追溯到dispatchPointerEvent方法当中,这个方法就是外部向View传递事件的调用。我们知道Android的UI界面中的所有View是一个树形的结构,因此这些事件也就会通过dispatchTouchEvent一层一层的往下传,从而每一个View都能够接收到事件,并决定是否处理。

dispatchPointerEvent是在ViewRootImpl当中调用,代码如下:

1
2
3
4
5
6
7
8
9
private int processPointerEvent(QueuedInputEvent q) {
 final MotionEvent event = (MotionEvent)q.mEvent;
 ...
 boolean handled = mView.dispatchPointerEvent(event);
 maybeUpdatePointerIcon(event);
 maybeUpdateTooltip(event);
 ...
 return handled ? FINISH_HANDLED : FORWARD;
}

在Activity中,它的根视图为DecorViewViewRootImpl在执行它的dispatchPointerEvent方法,它再向下把触摸事件依次向下传递。

除了触摸事件,按键事件也是类似,ViewRootImpl当中会调用View的dispatchKeyEvent方法,View当中会做相应的处理或者向下传递。

ViewRootImpl中对事件的处理

对于ViewRootImpl当中是如何获取事件,并且向后传递的,我们这里以触摸事件为主进行分析,其他事件也类似。

ViewRootImpl中,定义写一些内部类,大概如下:

classDiagram
class InputStage {
<<abstract>>
+InputStage mNext;
+deliver(QueuedInputEvent q)
#finish(QueuedInputEvent q, boolean handled)
#forward(QueuedInputEvent q)
#onDeliverToNext(QueuedInputEvent q)
#onProcess(QueuedInputEvent q)
}
class AsyncInputStage {
<<abstract>>
#defer(QueuedInputEvent q)
}
InputStage <|-- AsyncInputStage
AsyncInputStage <|--NativePreImeInputStage
InputStage <|-- ViewPreImeInputStage
AsyncInputStage <|-- ImeInputStage
InputStage <|-- EarlyPostImeInputStage
AsyncInputStage <|-- NativePostImeInputStage
InputStage <|-- ViewPostImeInputStage
InputStage <|--SyntheticInputStage

上面这几个类就ViewRootImpl中处理事件的类,其初始化代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//ViewRootImpl.java
public void setView(...) {
 ...
 CharSequence counterSuffix = attrs.getTitle();
 mSyntheticInputStage = new SyntheticInputStage();
 InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
 "aq:native-post-ime:" + counterSuffix);
 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
 InputStage imeStage = new ImeInputStage(earlyPostImeStage,
 "aq:ime:" + counterSuffix);
 InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
 InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
 "aq:native-pre-ime:" + counterSuffix);

 mFirstInputStage = nativePreImeStage;
 mFirstPostImeInputStage = earlyPostImeStage;
}

以上代码创建了多个InputStage,它们一起组成了输入事件处理的流水线。其中ViewPostImeInputStage中就会处理与触摸相关的事件,它的onProcess方法代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
protected int onProcess(QueuedInputEvent q) {
 if (q.mEvent instanceof KeyEvent) {
 return processKeyEvent(q);
 } else {
 final int source = q.mEvent.getSource();
 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
 return processPointerEvent(q);
 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
 return processTrackballEvent(q);
 } else {
 return processGenericMotionEvent(q);
 }
 }
}

可以看到,当我们的输入源为POINTER,触摸屏和鼠标的触发都是这一类。这个时候就会执行上面我们提到的 processPointerEvent方法,之后事件也就会传递到View当中。

这里我们知道了是通过InputStage的流水线拿到的事件,但是这个事件从何处来的呢,我们需要继续向上溯源。

ViewRootImpl从何处获得事件

关于这一点,我们仍然需要关注ViewRootImplsetView方法中的如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//VieRootImpl.java
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
 inputChannel = new InputChannel();
}
...
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
 getHostVisibility(), mDisplay.getDisplayId(), userId,
 mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
 mTempControls, attachedFrame, compatScale);
...
if (inputChannel != null) {

 mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
 Looper.myLooper());

}

在这里,我们创建了一个InputChannel,但是我们创建的InputChannel仅仅是java层的一个类,没法去获取到事件,随后我们调用WindowSessionaddToDisplayAsUser他就会获得mPtr,也就是Native层的InputChannel,具体内容随后再看相关代码。在15行,这里创建了一个WindowInputEventReceiver,它的参数为inputChannelLooper,这里一起看一下InputEventReceiver的构造方法,代码如下:

1
2
3
4
5
6
7
8
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
 mInputChannel = inputChannel;
 mMessageQueue = looper.getQueue();
 mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
 inputChannel, mMessageQueue);

 mCloseGuard.open("InputEventReceiver.dispose");
}

InputEventReceiver的初始化

这里主要是调用了nativeInit方法,并且获取到mReceivePtr,native的代码在android_view_InputEventReceiver.cpp当中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
 jobject inputChannelObj, jobject messageQueueObj) {
 std::shared_ptr<InputChannel> inputChannel =
 android_view_InputChannel_getInputChannel(env, inputChannelObj); //获取Native成的InputChannel
 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); //获取native层的消息队列

 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
 receiverWeak, inputChannel, messageQueue);
 status_t status = receiver->initialize();
 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // 增加引用计数
 return reinterpret_cast<jlong>(receiver.get());
}

在上面的代码中,先是分别获取了Native层的InputChannel和MessageQueue,之后创建了NativeInputEventReceiver,并且调用了它的initialize方法:

1
2
3
4
status_t NativeInputEventReceiver::initialize() {
 setFdEvents(ALOOPER_EVENT_INPUT);
 return OK;
}

内部调用了setFdEvents方法,参数ALOOPER_EVENT_INPUT,这个参数表示监听文件描述符的读操作,其内部代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void NativeInputEventReceiver::setFdEvents(int events) {
 if (mFdEvents != events) {
 mFdEvents = events;
 int fd = mInputConsumer.getChannel()->getFd();
 if (events) {
 mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
 } else {
 mMessageQueue->getLooper()->removeFd(fd);
 }
 }
}

这里就是拿到InputChannel的文件描述符,并且添加到Looper中去监听它的输入事件。我们暂时不会去阅读硬件层面的触发,以及事件如何发送到InputChannel当中,这里就大胆的假设,InputChannel当中有一个文件描述符,当有事件发生时候,会写入到这个文件当中去。而文件变化,Looper就会收到通知,事件也就发送出来了。

NativeInputEventReceiver 接收事件并分发

这个时候我们可以看一下NativeInputEventReceiver所实现的LooperCallbackhandleEvent方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {

 constexpr int REMOVE_CALLBACK = 0;
 constexpr int KEEP_CALLBACK = 1;

 if (events & ALOOPER_EVENT_INPUT) {
 JNIEnv* env = AndroidRuntime::getJNIEnv();
 status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
 mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
 return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
 }
 ...
 return KEEP_CALLBACK;
}

其中核心代码如上,就是判断如果事件为ALOOPER_EVENT_INPUT,则会调用consumeEvents方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
 bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
 ...
 ScopedLocalRef<jobject> receiverObj(env, nullptr);
 bool skipCallbacks = false;
 for (;;) {
 uint32_t seq;
 InputEvent* inputEvent;

 status_t status = mInputConsumer.consume(&mInputEventFactory,
 consumeBatches, frameTime, &seq, &inputEvent);
 ...
 assert(inputEvent);

 if (!skipCallbacks) {
 if (!receiverObj.get()) {
 receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
 if (!receiverObj.get()) {
 ...
 return DEAD_OBJECT;
 }
 }

 jobject inputEventObj;
 switch (inputEvent->getType()) {
 case AINPUT_EVENT_TYPE_MOTION: {
 MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
 if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
 *outConsumedBatch = true;
 }
 inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
 break;
 }
 ...
 default:
 assert(false); // InputConsumer should prevent this from ever happening
 inputEventObj = nullptr;
 }

 if (inputEventObj) {
 env->CallVoidMethod(receiverObj.get(),
 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
 ...
 env->DeleteLocalRef(inputEventObj);
 } else {
 ...
 }
 }
 }
}

上面的代码做过简化,switch的case只保留了一个。首先在第10行,我们看到这里调用了mInputConsumerconsume方法。这个InputConsumer是在Receiver创建的时候创建它,它用于到InputChannel中获取消息,并且按照类型包装成InputEvent的具体子类,并写入到inputEvent当中。在后面的Switch判断处,就可以根据它的类型做处理,从而封装成java类型的InputEvent。而receiverObj在第17行,通过jniGetReferent拿到java层的InputEventReceiver的引用,在41行调用了它的dispatchInputEvent方法,从而调用了java层的同名方法,代码如下:

1
2
3
4
private void dispatchInputEvent(int seq, InputEvent event) {
 mSeqMap.put(event.getSequenceNumber(), seq);
 onInputEvent(event);
}

我们再到WindowInputEventReceiver中看onInputEvent方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public void onInputEvent(InputEvent event) {
 List<InputEvent> processedEvents;
 try {
 processedEvents =
 mInputCompatProcessor.processInputEventForCompatibility(event);
 } finally {
 }
 if (processedEvents != null) {
 if (processedEvents.isEmpty()) {
 finishInputEvent(event, true);
 } else {
 for (int i = 0; i < processedEvents.size(); i++) {
 enqueueInputEvent(
 processedEvents.get(i), this,
 QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
 }
 }
 } else {
 enqueueInputEvent(event, this, 0, true);
 }
}

其中第4行代码,是为了兼容低版本设计的,只有应用的TargetSDKVersion小于23才会生效,这里我们就不关注它了。因此这里就只会执行第19行的代码,其内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void enqueueInputEvent(InputEvent event,
 InputEventReceiver receiver, int flags, boolean processImmediately) {
 QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 if (event instanceof MotionEvent) {
 MotionEvent me = (MotionEvent) event;
 }
 QueuedInputEvent last = mPendingInputEventTail;
 if (last == null) {
 mPendingInputEventHead = q;
 mPendingInputEventTail = q;
 } else {
 last.mNext = q;
 mPendingInputEventTail = q;
 }
 mPendingInputEventCount += 1;
 if (processImmediately) {
 doProcessInputEvents();
 } else {
 scheduleProcessInputEvents();
 }
}

这里的代码,把我们的Event包装成一个QueuedInputEvent,并且放置到mQueuedInputEventPool这个链表中,具体可以自行看obtainQueuedInputEvent方法。而根据我们之前传递的参数,可以看到这里后面会调用到doProcessInputEvents方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void doProcessInputEvents() {
 // Deliver all pending input events in the queue. 
 while (mPendingInputEventHead != null) {
 QueuedInputEvent q = mPendingInputEventHead;
 mPendingInputEventHead = q.mNext;
 if (mPendingInputEventHead == null) {
 mPendingInputEventTail = null;
 }
 q.mNext = null;

 mPendingInputEventCount -= 1; mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));

 deliverInputEvent(q);
 }
 //除了我们收到调用来把事件队列的所有事件消费,还有一些消息本来是准备通过Handler发送消息来处理的,既然我们已经手动把所有消息都处理掉了,那么如果有等待处理的消息事件,也就不需要了,下面的代码就是把他们删掉
 if (mProcessInputEventsScheduled) {
 mProcessInputEventsScheduled = false;
 mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
 }
}

这里的代码主要就是遍历之前的链表,把每一条消息都取出来,并且调用deliverInputEvent方法来把它分发掉,同时会把它从链表中删除。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void deliverInputEvent(QueuedInputEvent q) {
 try {
 if (mInputEventConsistencyVerifier != null) {
 try { //事件一致性检查,避免外面传过来应用无法处理的事件
 mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
 } finally {
 }
 }

 InputStage stage; //选择要使用的入口InputStage
 if (q.shouldSendToSynthesizer()) {
 stage = mSyntheticInputStage;
 } else {
 stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
 }
 ...
 if (stage != null) {
 handleWindowFocusChanged();
 stage.deliver(q); //InputStage 开始分发事件
 } else {
 finishInputEvent(q);
 }
 } finally {

 }
}

在这个方法中,主要就是根据事件的属性选择入口的InputStage,之后调用它的deliver方法,在这个方法中就会按照链式调用,最终能够处理掉的一个InputStage会将它处理,也就是把事件分发到应用中去。

到这里我们就完成了从InputChannel中获取事件,并且通过InputEventReceiver传递到Java层,并且通过InputStage转发到应用的View当中。

InputChannel的初始化

刚刚我们已经基本把事件处理在ViewRootImpl中的部分看完了,而我们在其中创建的InputChannel只是一个壳,想要看看它的真正的初始化,我们沿着之前调用的addToDisplayAsUser继续往后看。IWindowSession是一个AIDL定义的Binder服务,在它的定义中InputChannel使用了out进行修饰,表示它会被binder服务端修改,并写入数据。而这个addToDisplayAsUser方法内部最终会调用WMS的addWindow方法,其中和InputChannel相关代码如下:

1
2
3
4
5
6
7
8
final WindowState win = new WindowState(this, session, client, token, parentWindow,
 appOp[0], attrs, viewVisibility, session.mUid, userId,
 session.mCanAddInternalSystemWindow);
 final boolean openInputChannels = (outInputChannel != null
 && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
 win.openInputChannel(outInputChannel);
}

这里outInputChannel就是我们从客户端传过来的那个InputChannel的壳,随后便调用了WindowStateopenInputChannel方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void openInputChannel(InputChannel outInputChannel) {
 String name = getName(); //获取window的name
 mInputChannel = mWmService.mInputManager.createInputChannel(name); //创建
 mInputChannelToken = mInputChannel.getToken();
 mInputWindowHandle.setToken(mInputChannelToken);
 mWmService.mInputToWindowMap.put(mInputChannelToken, this);
 if (outInputChannel != null) {
 mInputChannel.copyTo(outInputChannel); //将Native Channel写入我们传入的InputChannel
 } else {
 }
}

这里就是调用InputManager去创建InputChannel,并且把它和我们的WIndow关联,以及保存到我们传入的InputChannel当中,这样我们的View层面就可以通过InputChannel获取到事件了。InputManagerService创建InputChannel的部分这里就不讨论了,留待以后讨论。

总结

到此为止,就分析完了应用侧从WMS到View,如何初始化InputEventReceiver,InputEventReceiver和InputChannel关联起来,事件如何从InputChannel一直传递到我们的View的了。

sequenceDiagram
InputChannel-->>NativeInputEventReceiver: handleEvent
note right of InputChannel: notify has event via Looper
NativeInputEventReceiver->> NativeInputEventReceiver: consumeEvents
NativeInputEventReceiver->>+ InputChannel: consume
note right of InputChannel: get event from InputChannel
InputChannel-->>-NativeInputEventReceiver: return inputEvent
NativeInputEventReceiver->>InputEventReceiver: dispatchInputEvent
InputEventReceiver->>InputEventReceiver: onInputEvent
InputEventReceiver->>ViewRootImpl: enqueueInputEvent
ViewRootImpl->>ViewRootImpl: doProcessInputEvents
ViewRootImpl->>ViewRootImpl: deliverInputEvent
ViewRootImpl->>+ViewPostImeInputStage: deliver
ViewPostImeInputStage->>ViewPostImeInputStage:onProcess
ViewPostImeInputStage->>ViewPostImeInputStage: processPointerEvent
ViewPostImeInputStage-->>+View: dispatchPointerEvent
View->>View:dispatchTouchEvent
View->>View: onTouch
View-->>-ViewPostImeInputStage: return is consume it or not
ViewPostImeInputStage-->>-ViewRootImpl: finish deliver

之前的分析涉及到了InputChannel的初始化和InputEventReceiver的初始化,直接看可以会比较绕人,上面从事件分发角度画了一下事件从InputChannel一直流转到View的一个时序图,希望对于你理解这个流程有所理解。如果哪里存在疏漏,也欢迎读者朋友们评论指点。

看完评论一下吧

Android源码分析:Window与Activity与View的关联

2024年9月19日 21:30

Activity是四大组件中和UI相关的那个,应用开发过程中,我们所有的界面基本都需要使用Activity才能去渲染和绘制UI,即使是ReactNative,Flutter这种跨平台的方案,在Android中,也需要一个Activity来承载。但是我们的Activity内我们设置的View又是怎么渲染到屏幕上的呢,这背后又有WindowManager和SurfaceFlinger来进行工作。本文就来看看WindowManger如何管理Window,以及Window如何与Activity产生关系的呢。

Activity与Window的初见

Activity的创建是在ActivityThreadperformLaunchActivity中,这里会创建要启动的Activity,并且会调用Activity的attach方法,在这个方法当中就会创建Window,其中和Window相关的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
 mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
 mWindow.setUiOptions(info.uiOptions);
}
mWindow.setWindowManager(
 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
 mToken, mComponent.flattenToString(),
 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
 mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

这里我们可以看到为Activity创建了Window,目前Android上面的Window实例为PhoneWindow,同时还给Window设置了WindowManager,不过这里的WindowManager仅仅是一个本地服务,它的实现为WindowManagerImpl,它的注册代码在SystemServiceRegister.java中,代码如下:

1
2
3
4
5
6
registerService(Context.WINDOW_SERVICE, WindowManager.class,
 new CachedServiceFetcher<WindowManager>() {
 @Override
 public WindowManager createService(ContextImpl ctx) {
 return new WindowManagerImpl(ctx);
 }});

而我们这个WindowManagerImpl内部持有持有了一个WindowManagerGlobal,看名字就知道它应该会涉及到跨进程通讯,去看它代码就知道它内部有两个成员,分别是sWindowManagerServicesWindowSession,这两个成员就用于跨进程通讯。这里我们先知道有这几个类,后面到用处再继续分析。

---
title: WindowManager相关类图
---
classDiagram
directioni TB
class ViewManager {
<<interface>>
addView(view, params)
updateViewLayout(view, params)
removeView(view)
}
class WindowManager {
<<interface>>
}
class WindowManagerImpl {
- WindowManagerGlobal mGlobal;
- IBinder mWindowContextToken;
- IBinder mDefaultToken;
}
ViewManager <|-- WindowManager
WindowManager <|.. WindowManagerImpl
class WindowManagerGlobal {
IwindowManager sWindowManagerService;
IWindowSession SwindowSession;
}
WindowManagerImpl ..> WindowManagerGlobal
class Window {
<<abstract>>
WindowManager mWindowManager;
}
Window <|.. PhoneWindow
Window ..> WindowManager
class IWindow {
<<Interface>>
}
IWindow <|--W
class ViewRootImpl {
W mWindow
IWindowSession mWindowSession
}
ViewRootImpl .. W
ViewRootImpl .. IWindowSession
IWindowSession .. W

这里只可出了App进程相关的一些类,System_Server相关未列出,后面涉及到相关部分的时候再进行分析。

Window与View的邂逅

我们一般情况下会在Activity的onCreate当中去调用setContentView,只有这样我们的View才能够显示出来。因此我们直接看这个方法的调用:

1
2
3
4
public void setContentView(@LayoutRes int layoutResID) {
 getWindow().setContentView(layoutResID);
 initWindowDecorActionBar();
}

其中就是调用了window的同名方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public void setContentView(int layoutResID) {
 if (mContentParent == null) {
 installDecor();
 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
 mContentParent.removeAllViews();
 }

 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
 final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
 getContext());
 transitionTo(newScene); //执行页面Transition动画
 } else {
 mLayoutInflater.inflate(layoutResID, mContentParent);
 }
 mContentParent.requestApplyInsets();
 final Callback cb = getCallback();
 if (cb != null && !isDestroyed()) {
 cb.onContentChanged();
 }
 mContentParentExplicitlySet = true;
}

这里我们主要是将我们的ContentView添加到mContentParent当中去,这个mContentParent有可能为空,需要我们通过installDecor来创建,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private void installDecor() {
 mForceDecorInstall = false;
 if (mDecor == null) {
 mDecor = generateDecor(-1);
 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
 mDecor.setIsRootNamespace(true);
 if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
 }
 } else {
 mDecor.setWindow(this);
 }
 if (mContentParent == null) {
 mContentParent = generateLayout(mDecor);

 // Set up decor part of UI to ignore fitsSystemWindows if appropriate. 
 mDecor.makeFrameworkOptionalFitsSystemWindows();

 final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
 R.id.decor_content_parent);

 if (decorContentParent != null) {
 mDecorContentParent = decorContentParent;
 mDecorContentParent.setWindowCallback(getCallback());
 if (mDecorContentParent.getTitle() == null) {
 mDecorContentParent.setWindowTitle(mTitle);
 }

 final int localFeatures = getLocalFeatures();
 for (int i = 0; i < FEATURE_MAX; i++) {
 if ((localFeatures & (1 << i)) != 0) {
 mDecorContentParent.initFeature(i);
 }
 }

 mDecorContentParent.setUiOptions(mUiOptions);

 ...

 PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
 if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
 invalidatePanelMenu(FEATURE_ACTION_BAR);
 }
 } else {
 mTitleView = findViewById(R.id.title);
 if (mTitleView != null) {
 //title view的设置
 }
 }

 if (mDecor.getBackground() == null && mBackgroundFallbackDrawable != null) {
 mDecor.setBackgroundFallback(mBackgroundFallbackDrawable);
 }

 if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
 ...
 //页面动画的读取和设置 
 }
 }
}

这里我们可以看到主要做的就是创建了decorView和ContentParent,还有一些动画,标题之类的初始化我们这里就跳过了。DecorView就是App Activity页面最底层的容器,它为我们封装了状态栏,底部导航栏,App页面的内容的展示。而ContentParent的初始化,则是根据Activity的设置,根据是否展示状态栏,是否展示标题栏等,进行加载相应的布局,加载到DecorView当中,最后com.android.internal.R.id.content对应的FrameLayout就会成为ContentParent。 当这一切做完,我们的页面View就成功的添加到Window当中了,但是它是如何展示出来的呢,还需要继续往后看。我们需要前往ActivityThread的handleResumeActivity方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//调用Activity的onResume方法
if (!performResumeActivity(r, finalStateRequest, reason)) {
 return;
}
//r为ActivityClientRecord
final Activity a = r.activity;
//检查当前的Activity是否能显示
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
 willBeVisible = ActivityClient.getInstance().willActivityBeVisible(
 a.getActivityToken());
}
if (r.window == null && !a.mFinished && willBeVisible) {
 r.window = r.activity.getWindow(); //把activity的window保存到r.window中
 View decor = r.window.getDecorView();
 decor.setVisibility(View.INVISIBLE);
 ViewManager wm = a.getWindowManager();
 WindowManager.LayoutParams l = r.window.getAttributes();
 a.mDecor = decor;
 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 l.softInputMode |= forwardBit;
 if (r.mPreserveWindow) {
 a.mWindowAdded = true;
 r.mPreserveWindow = false;
 ViewRootImpl impl = decor.getViewRootImpl();
 if (impl != null) {
 impl.notifyChildRebuilt();
 }
 }
 if (a.mVisibleFromClient) {
 if (!a.mWindowAdded) {
 a.mWindowAdded = true;
 wm.addView(decor, l); //调用windowManager添加decorView
 } else {
 a.onWindowAttributesChanged(l);
 }
 }
} else if (!willBeVisible) {
 r.hideForNow = true;
}

可以看到上面的代码把window保存到了ActivityClientRecord当中,同时调用了WindowManager的addView方法,去添加view。我们继续往后看代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
 ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
 WindowManager.LayoutParams l = impl != null
 ? impl.mWindowAttributes : r.window.getAttributes();
 if ((l.softInputMode
 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
 != forwardBit) {
 l.softInputMode = (l.softInputMode
 & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
 | forwardBit;
 if (r.activity.mVisibleFromClient) {
 ViewManager wm = a.getWindowManager();
 View decor = r.window.getDecorView();
 wm.updateViewLayout(decor, l);
 }
 }

 r.activity.mVisibleFromServer = true;
 mNumVisibleActivities++;
 if (r.activity.mVisibleFromClient) {
 r.activity.makeVisible();
 }

 if (shouldSendCompatFakeFocus) {
 if (impl != null) {
 impl.dispatchCompatFakeFocus();
 } else {
 r.window.getDecorView().fakeFocusAfterAttachingToWindow();
 }
 }
}

上面的代码中,我们看到主要做了两件事情,一个是调用updateViewLayout去更新视图的属性,但是updateViewLayout也要属性发生变化,并且有输入法的时候才会执行,另外就是调用activity的makeVisible方法去展示View。

这个过程我们需要分析如下两步。

  1. 调用addView添加decorView
  2. 调用activity.makeVisible来显示 我们分别看一下这两个方法的实现

WMS与ViewRootImpl的遇见:调用WindowManger的addView

1
2
3
4
5
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
 applyTokens(params);
 mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
 mContext.getUserId());
}

这里就是调用mGlobaladdView方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void addView(View view, ViewGroup.LayoutParams params,
 Display display, Window parentWindow, int userId) {

 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
 ....

 ViewRootImpl root;
 View panelParentView = null;

 synchronized (mLock) {

 int index = findViewLocked(view, false);
 ...

 if (windowlessSession == null) {
 root = new ViewRootImpl(view.getContext(), display);
 } else {
 root = new ViewRootImpl(view.getContext(), display,
 windowlessSession, new WindowlessWindowLayout());
 }

 view.setLayoutParams(wparams);

 mViews.add(view);
 mRoots.add(root);
 mParams.add(wparams);

 try {
 root.setView(view, wparams, panelParentView, userId);
 } catch (RuntimeException e) {
 final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
 // BadTokenException or InvalidDisplayException, clean up. 
 if (viewIndex >= 0) {
 removeViewLocked(viewIndex, true);
 }
 throw e;
 }
 }
}

在正常的App页面,windowlessSession会一直为空,这里就会创建一个ViewRootImpl,并且把我们的DecorView以及WindowParams都传进去。并且viewrootwparams都会按照顺序存到List当中。这里我们需要去看ViewRootImpl的setView方法,其中和添加到屏幕相关的代码如下:

1
2
3
4
5
6
7
requestLayout(); //测量布局
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
 getHostVisibility(), mDisplay.getDisplayId(), userId,
 mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
 mTempControls, attachedFrame, compatScale);
...
view.assignParent(this); //将ViewRootImpl设置为DecorView的parent

这里的mDisplay为外面从Context中所获取的,用于指定当前的UI要显示到哪一个显示器上去。这里的mWindowSession的获取代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
 synchronized (WindowManagerGlobal.class) {
 if (sWindowSession == null) {
 try {
 @UnsupportedAppUsage
 InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
 IWindowManager windowManager = getWindowManagerService();
 sWindowSession = windowManager.openSession(
 new IWindowSessionCallback.Stub() {
 @Override
 public void onAnimatorScaleChanged(float scale) {
 ValueAnimator.setDurationScale(scale);
 }
 });
 } catch (RemoteException e) {
 throw e.rethrowFromSystemServer();
 }
 }
 return sWindowSession;
 }
}

可以看到就是通过IWindowManger这个Binder服务调用了openSession来获取了一个WindowSession。其代码如下:

1
2
3
4
//WindowManagerService.java
public IWindowSession openSession(IWindowSessionCallback callback) {
 return new Session(this, callback);
}

在System_Server端,创建了一个Session对象来提供相关的服务。它的addToDisplayAsUser方法又调用了WMSaddWindow方法,这个方法比较长我们只看其中和UI展示相关的部分,并且UI类型不是App的普通UI的也都给省略掉。

1
2
3
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
 appOp);
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

第一行代码首先是去检查我们当前要展示的view,它的类型是否支持去展示。第3行代码的内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
 if (token != null) {
 final WindowToken wToken = mRoot.getWindowToken(token);
 if (wToken != null) {
 return wToken.getDisplayContent();
 }
 }

 return mRoot.getDisplayContentOrCreate(displayId);
}

mRoot为一个RootWindowContainer对象,之前我们在分析Activity的启动过程中已经见到了它,我们的ActivityRecord和Task都存在它当中。这里wToken初始情况一般为null因此会执行下面的getDisplayContentOrCreate方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DisplayContent getDisplayContentOrCreate(int displayId) {
 DisplayContent displayContent = getDisplayContent(displayId);
 if (displayContent != null) {
 return displayContent;
 }
 ...
 final Display display = mDisplayManager.getDisplay(displayId);
 ...
 displayContent = new DisplayContent(display, this);
 addChild(displayContent, POSITION_BOTTOM);
 return displayContent;
}

这里就是根据displayId从列表中去拿DisplayContent如果不存在就去创建一个并且保存到列表中,方便下次使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//WindowManagerService.addWindow
WindowToken token = displayContent.getWindowToken(
 hasParent ? parentWindow.mAttrs.token : attrs.token);
if (token == null) {
{
 ...
 final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
 token = new WindowToken.Builder(this, binder, type)
 .setDisplayContent(displayContent)
 .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
 .setRoundedCornerOverlay(isRoundedCornerOverlay)
 .build();
}

继续看addWindow的内容,如果displayContent是新创建的,那么这里拿到的token就会为空,因此这里调用了client.asBinder来获取IBinder,或者直接拿’attr’中的token,这个client为IWindow类型,它在应用侧为W的实例,它是ViewRootImpl的一个内部类。这里创建完WindowToken之后,我们可以继续往后看。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//WindowManagerService.addWindow
final WindowState win = new WindowState(this, session, client, token, parentWindow,
 appOp[0], attrs, viewVisibility, session.mUid, userId,
 session.mCanAddInternalSystemWindow);
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid,
 callingPid);
win.setRequestedVisibilities(requestedVisibilities);

res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);

这里创建的WindowState用于保存Window的状态,可以说是Window在WMS中存储的一个章台。随后从DisplayContent中拿到了DisplayPolicy这个类主要是用于控制显示的一些行为,比如状态栏,导航栏的显示状态之类的。这里会根据WindowParams来调整DisplayPolicy的参数,以及调用validateAddingWindowLw检查当前的window是否能够添加的系统界面中,这个app普通type不涉及。

1
2
3
4
//WindowManagerService.addWindow
win.attach();
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();

win.attach方法如下:

1
2
3
void attach() {
 mSession.windowAddedLocked();
}

其中就调用了SessionwindowAddedLocked方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void windowAddedLocked() {
 if (mPackageName == null) {
 final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid);
 if (wpc != null) {
 mPackageName = wpc.mInfo.packageName;
 mRelayoutTag = "relayoutWindow: " + mPackageName;
 } else {
 }
 }
 if (mSurfaceSession == null) {
 mSurfaceSession = new SurfaceSession();
 mService.mSessions.add(this);
 if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
 mService.dispatchNewAnimatorScaleLocked(this);
 }
 }
 mNumWindow++;
}

对于每个进程第一次使用openSession创建的Session这个地方都会执行,主要就是创建了SurfaceSession,并且保存到WMSmSessions当中去。之后又把client作为key,WindowState为value存放到mWindowMap当中。

1
2
3
//WindowManagerService.addWindow
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);

先看这个WindowToken.addWindow方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void addWindow(final WindowState win) {
 if (mSurfaceControl == null) {
 createSurfaceControl(true /* force */);

 reassignLayer(getSyncTransaction());
 }
 if (!mChildren.contains(win)) {
 addChild(win, mWindowComparator);
 mWmService.mWindowsChanged = true;
 }
}

这里创建了一个SurfaceControl,并且保存到了WindowList当中去。随后再看displayyPolicy.addWindowLw,其中主要用于处理inset相关的处理,这里也先跳过。到此位置addView的代码基本就看完了。

调用activity.makeVisible来显示

1
2
3
4
5
6
7
8
void makeVisible() {
 if (!mWindowAdded) {
 ViewManager wm = getWindowManager();
 wm.addView(mDecor, getWindow().getAttributes());
 mWindowAdded = true;
 }
 mDecor.setVisibility(View.VISIBLE);
}

我们之前已经分析过addView了,这里mWindowAdded也是为true,这里的addView因此是不会被执行的。我们看一下下面的setVisibility,这个就是我们的普通View的方法,还是直接看源码:

1
2
3
4
//View.java
public void setVisibility(@Visibility int visibility) {
 setFlags(visibility, VISIBILITY_MASK);
}

这里是直接调用了setFlags方法,其中和设置显示相关的部分如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE) {
 if ((changed & VISIBILITY_MASK) != 0) {
 mPrivateFlags |= PFLAG_DRAWN;
 invalidate(true);

 needGlobalAttributesUpdate(true);
 shouldNotifyFocusableAvailable = hasSize();
 }
}

if ((changed & VISIBILITY_MASK) != 0) {
 if (mParent instanceof ViewGroup) {
 ViewGroup parent = (ViewGroup) mParent;
 parent.onChildVisibilityChanged(this, (changed & VISIBILITY_MASK),
 newVisibility);
 parent.invalidate(true);
 } else if (mParent != null) {
 mParent.invalidateChild(this, null);
 }
}

DecorView的parent为ViewRootImpl,因此上面会调用ViewRootImplinvalidateChild方法,内部会调用如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
 checkThread();

 if (dirty == null) {
 invalidate();
 return null;
 } else if (dirty.isEmpty() && !mIsAnimating) {
 return null;
 }

 if (mCurScrollY != 0 || mTranslator != null) {
 mTempRect.set(dirty);
 dirty = mTempRect;
 if (mCurScrollY != 0) {
 dirty.offset(0, -mCurScrollY);
 }
 if (mTranslator != null) {
 mTranslator.translateRectInAppWindowToScreen(dirty);
 }
 if (mAttachInfo.mScalingRequired) {
 dirty.inset(-1, -1);
 }
 }

 invalidateRectOnScreen(dirty);

 return null;
}

这段代码会检查需要从新绘制的区域,并且放在dirty当中,最后调用invalidateRectOnScreen方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private void invalidateRectOnScreen(Rect dirty) {
 final Rect localDirty = mDirty;

 // Add the new dirty rect to the current one 
 localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
 final float appScale = mAttachInfo.mApplicationScale;
 final boolean intersected = localDirty.intersect(0, 0,
 (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
 if (!intersected) {
 localDirty.setEmpty();
 }
 if (!mWillDrawSoon && (intersected || mIsAnimating)) {
 scheduleTraversals();
 }
}

这里仍然检查dirty区域,并且去做Traversal。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void scheduleTraversals() {
 if (!mTraversalScheduled) {
 mTraversalScheduled = true;
 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
 mChoreographer.postCallback(
 Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
 notifyRendererOfFramePending();
 pokeDrawLockIfNeeded();
 }
}

这里就是启动线程去不断的页面的刷新重绘,就不分析了。最终会执行到performTraversals方法,其中有如下代码我们比较关注:

1
2
3
4
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
 || mForceNextWindowRelayout) {
 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}

当首次执行这个方法的时候mFirst为true,除了这个条件之外,window需要从新计算size,view的可见性变化,windowParams变化等任一条件满足就会执行这里。我们在继续看里面的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if (relayoutAsync) {
 mWindowSession.relayoutAsync(mWindow, params,
 requestedWidth, requestedHeight, viewVisibility,
 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
 mLastSyncSeqId);
} else {
 relayoutResult = mWindowSession.relayout(mWindow, params,
 requestedWidth, requestedHeight, viewVisibility,
 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
 mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
 mTempInsets, mTempControls, mRelayoutBundle);
 ...
}

当view为本地进行Layout且一些其他的条件符合,并且它的位置大小没有变化的时候,才会是relayoutAsync,不过两个最终的在服务端都会调用relayout方法,区别就是这里relayout的时候传过去了一个mSurfaceControl,这个接口是AIDL定义的,这个参数定义的为out,服务端会传输值到这个对象里,我们随后会看到,因为非异步是大多数情况的调用,这里也对他进行分析。在Session的relayout方法中调用了如下代码:

1
2
3
4
int res = mService.relayoutWindow(this, window, attrs,
 requestedWidth, requestedHeight, viewFlags, flags, seq,
 lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
 outActiveControls, outSyncSeqIdBundle);

这里就是调用了WMSrelayoutWindow方法,其中我们关注的有一下代码:

1
2
3
4
5
6
7
8
9
final WindowState win = windowForClientLocked(session, client, false);
if (shouldRelayout && outSurfaceControl != null) {
 try {
 result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
 } catch (Exception e) {
 ...
 return 0;
 }
}

为应用提供画布容器

这里看一下这个createSurfaceControl的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
WindowSurfaceController surfaceController;
try {
 surfaceController = winAnimator.createSurfaceLocked();
} finally {

}
if (surfaceController != null) {
 surfaceController.getSurfaceControl(outSurfaceControl);

} else {
 outSurfaceControl.release();
}

第三行主要是创建一个WindowSurfaceController对象,第8行则是使用这个对象去获取SurfaceControl,我们看一下它的代码:

1
2
3
void getSurfaceControl(SurfaceControl outSurfaceControl) {
 outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}

SurfaceControlcopyFrom方法代码如下:

1
2
3
4
5
6
7
public void copyFrom(@NonNull SurfaceControl other, String callsite) {
 mName = other.mName;
 mWidth = other.mWidth;
 mHeight = other.mHeight;
 mLocalOwnerView = other.mLocalOwnerView;
 assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}

最主要的是最后的assignNativeObject赋值到我们从app进程传过来的SurfaceControl当中。native层的SurfaceControl有如下几个成员变量:

1
2
3
4
5
6
sp<SurfaceComposerClient> mClient;
sp<IBinder> mHandle;
sp<IGraphicBufferProducer> mGraphicBufferProducer;
mutable Mutex mLock;
mutable sp<Surface> mSurfaceData;
mutable sp<BLASTBufferQueue> mBbq;

其中就有Surface,而我们在服务端拿到的这个SurfaceControl随后会写回客户端,这样App进程就可以把UI元素绘制到这个Surface上面了。

前面有列过客户端WindowManager相关的类,这里在列一下system_server进程中相关的类:

classDiagram
class IWindowManager {
<<interface>>
}
class Stub["IWindowManager.Stub"]
IWindowManager <|..Stub
class WindowManagerService {
WindowManagerPolicy mPolicy
ArraySet~Session~ mSessions
HashMap~IBinder, WindowState~ mWindowMap
RootWindowContainer mRoot
}
Stub <|--WindowManagerService
class IWindowSession {
<<Interface>>
}
class SessionStub["IWindowSession.Stub"] {
<<abstract>>
}
class Session {
WindowManagerService mService
}
IWindowSession <|..SessionStub
SessionStub<|--Session
WindowContainer<|--RootWindowContainer
WindowContainer <|-- WindowToken
WindowContainer <|--WindowState
WindowToken .. WindowManagerService
Session .. WindowManagerService
RootWindowContainer .. WindowManagerService

总结

我们在调用WMS的addWindow的时候,并没有把View直接传过来,所传过来的WindowLayoutParams当中,宽和高是比较重要的信息,因为在对调用这个方法之前,代码中先是执行了requestLayout去测量的布局的尺寸,并且在返回参数中通过Rect返回了画布的尺寸。我们也知道通过SurfaceControl为我们提供了Surface,这样客户端就能够把UI数据写上去了。而这样,这个Window与View就能够与系统的其他服务一起,把我们的UI显示到屏幕上了。

在与WMS初始通信的时候,WMS服务端为App创建了Session这个对象,App通过这个对象来与服务端进行Binder通讯。同时,App进程在创建ViewRootImpl的时候创建了W这个对象,它是IWindow的binder对象,服务端可以通过这个对象来与app进程通讯。为了方便理解,关于服务端和客户端,我又画了如下图,希望对你理解它们有所帮助。

以上就应用的窗口与Activity相关的分析,整体流程还是比较复杂的,如果哪里存在疏漏,也欢迎读者朋友们评论指点。另外关于应用的事件分发也会涉及到WMS和ViewRootImpl,为了使得文章不至于太长,就留到下次再进行分析。

看完评论一下吧

Android源码分析:Activity启动流程Task相关分析

2024年9月13日 17:57

Activity的启动分析,很大一块需要了解的是Activity的Task管理,以及启动过程中Task的决策,在之前分析启动流程中,关于Task处理的部分,我这里是简化掉了很多的,今天再来分析一下。

入口与计算启动参数

在之前分析Activity的启动中,已经看到了关于处理Task的代码是在ActivityStart当中的startActivityInner方法当中,这个方法有不少入参,先捋一遍: resultTo为调用的Activity的mToken(IBinder)

ActivityRecord r, //新创建的Record,包含calling信息和要打开的ActivityInfo等
ActivityRecord sourceRecord, //resultTo不为空的时候才会去使用`ActivityRecord isInAnyTask`读取
IvoiceInteractionSession voiceSession, //startVoiceActivity的时候才会传
IvoiceInteractor voiceInteractor, //同上,一般为系统的语音助手界面
int startFlags, //客户端传过来的startFlags一般为0
boolean doResume, //是否需要去resume activity,对于启动Activity场景总是为true
ActivityOptions options, //Activity启动的一些参数,页面跳转动画等
Task inTask, //一般为通过AppTaskImpl启动Activity才会设置值,正常app启动不存在
TaskFragment inTaskFragment, //同上,一般情况为空
int balCode, //Activity后台启动的许可Code,默认为BAL_ALLOW_DEFAULT
NeededGrants intentGrants //Intent访问权限授权

有了所有的入参可以看看computeLaunchingTaskFlags,对于普通应用mInTask为空,mSourceRecord不为空,关注这个方法内的如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
if (mInTask == null) {
 if (mSourceRecord == null) {
 if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
 //如果获取不到启动来源的ActivityRecord,且当前要启动的Activity还没有设置NEW_TASK flag,则给他添加
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 }
 } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
 //如果来源ActivityRecord是SINGLE INSTANCE,也就是说它是自己独立的任务栈,新启动Activity必须设置NEW_TASK 
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
 //如果新启动的Activity是SingleInstance或者SingleTask,也要添加NEW_TASK flag
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 }
}

if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
 && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
 //如果要启动的Activity设置了分屏的FLAG,但是却没有设置NEW——FLAG或者没有源ActivityRecord,这个时候就需要忽略掉分屏的这个FLAG
 mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
}

简化版本的流程图如下:

获取当前的顶部Task: getFocusedRootTask

以上是针对LaunchFlag的一部分处理,但并不是全部,暂时继续往后看。随后就是获取task

1
2
3
final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
final Task reusedTask = getReusableTask();

首先看这个mPreferredTaskDisplayArea,这个表示倾向的Activity显示的TaskDisplay,它的赋值是在前面的setInitialState方法中:

1
2
3
4
5
mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
 sourceRecord, options, mRequest, PHASE_DISPLAY, mLaunchParams);
mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
 ? mLaunchParams.mPreferredTaskDisplayArea
 : mRootWindowContainer.getDefaultTaskDisplayArea();

我们这里就以它是拿的DefaultTaskDisplayArea为例来分析,继续就是看它的getFocusedRootTask,看代码之前先看看这些类的关系图,之前画过Task,WindowContainer相关的,但是还不够全,这里再补充完整一点。

classDiagram
class ConfigurationContainer {
<<abstract>>
}
class WindowContainer {
List<WindowContainer> mChildren
}
class TaskFragment
class Task
class ActivityRecord {
Task task
TaskDisplayArea mHandoverTaskDisplayArea
}
ConfigurationContainer <|--WindowContainer
WindowContainer <|-- TaskFragment
TaskFragment <|--Task
WindowContainer <|--RootWindowContainer
WindowContainer <|-- DisplayArea
DisplayArea <|-- TaskDisplayArea
WindowContainer"0..*" <-- "1*"WindowContainer
WindowContainer <|-- WindowToken
WindowToken <|--ActivityRecord
ActivityRecord --> Task
ActivityRecord --> TaskDisplayArea

当然WindowContainer的子类远不止这些,包括WindowState等等都是它的子类,但是暂时不需要讨论他们,这里暂时先不列出来了。 我们还是先看getFocusedRootTask方法的源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Task getFocusedRootTask() {
 if (mPreferredTopFocusableRootTask != null) {
 return mPreferredTopFocusableRootTask;
 }

 for (int i = mChildren.size() - 1; i >= 0; --i) {
 final WindowContainer child = mChildren.get(i);
 if (child.asTaskDisplayArea() != null) {
 final Task rootTask = child.asTaskDisplayArea().getFocusedRootTask();
 if (rootTask != null) {
 return rootTask;
 }
 continue;
 }

 final Task rootTask = mChildren.get(i).asTask();
 if (rootTask.isFocusableAndVisible()) {
 return rootTask;
 }
 }

 return null;
}

如果说当前的TaskDisplayArea中,preferredTopFocusableRoot存在就会直接使用,这个会在postionChildTaskAt的时候,如果child放置到顶部,并且它是可获得焦点的,会把他赋值给这个preferredTopFocusableRoot。 我们这里先看它为空的情况。如果它为空,这回到树状结构中查找,遍历树节点如果也是TaskDisplayArea,则会 看他们的focusedRootTask是否存在,如果就返回。如果节点是Task,就会检查这个Task是否为可获得焦点并且可见的,则返回它。否则就返回空。因为我们当前已经打了Activity,这里一般是可以获得值的。

如果拿到了prevTopRootTask,就会去调用getTopLeafTask去获取叶子节点的Task,代码如下:

1
2
3
4
5
6
7
8
public Task getTopLeafTask() {
 for (int i = mChildren.size() - 1; i >= 0; --i) { //从大数开始遍历
 final Task child = mChildren.get(i).asTask();
 if (child == null) continue; //如果不是Task就跳过
 return child.getTopLeafTask(); //继续看它的子节点
 }
 return this; //没有孩子节点,那就是一个叶子节点 
}

以上是获取叶子节点的代码,典型的树的遍历代码。到目前是拿的当前在展示的页面的任务栈。

获取可复用的Task:getReusableTask

而之后的getReusableTask则是获取可以使用的任务Task:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private Task getReusableTask() {
 //一般是从最近任务打开的页面才会执行这里,我们可以跳过
 if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
 Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
 if (launchTask != null) {
 return launchTask;
 }
 return null;
 }
 //如果启动的FLAG是 Single Instance或者SingleTask;又或者是虽然设置了NEW_TASK但是没有设置MULTIPLE_TASK。这些情况都会把新的Activity放到已有的任务栈。
 boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
 (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
 || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
 //因为mInTask为空,后面的resultTo不为空,因此putIntoExistingTask结果为false。当通过startActivityForResult的且requestCode > 0 时候就不为空
 putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
 ActivityRecord intentActivity = null;
 if (putIntoExistingTask) {
 if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
 //这种情况只有一个实例,就通过intent和activityInfo去找到它。
 intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
 mStartActivity.isActivityTypeHome());
 } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
 //对于分屏的,如果历史栈中有才使用
 intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
 !(LAUNCH_SINGLE_TASK == mLaunchMode));
 } else {
 // 查找最合适的Task给Activity用 
 intentActivity =
 mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
 }
 }

 if (intentActivity != null && mLaunchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK
 && !intentActivity.getTask().getRootActivity().mActivityComponent.equals(
 mStartActivity.mActivityComponent)) {
 //对于singleInstancePreTask,如果Task的根Activity不是要启动的Activity那么还是不能够复用,因此需要把intentActivity设置为空。
 intentActivity = null;
 }

 if (intentActivity != null
 && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
 && intentActivity.getDisplayArea() != mPreferredTaskDisplayArea) {
 //
 intentActivity = null;
 }

 return intentActivity != null ? intentActivity.getTask() : null;
}

以上就是根据条件判断是否可以复用栈,如果可以会去拿已经存在的Activity,如果Activity存在,则回去拿它的Task。其中这里有一个singleInstancePreTask的启动模式,这个对于我们很多Android开发这是不熟悉的,它是Android12引入的,它可以说是加强版本的singleInstance,当它是Task栈的根Task的时候就复用,如果不是的就类似singleTask会去打开一个新的Task栈。

这里先来看一下这个findActivity,他也是到RootWindowContainer中去查找,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
 ComponentName cls = intent.getComponent();
 if (info.targetActivity != null) {
 cls = new ComponentName(info.packageName, info.targetActivity);
 }
 final int userId = UserHandle.getUserId(info.applicationInfo.uid);

 final PooledPredicate p = PooledLambda.obtainPredicate(
 RootWindowContainer::matchesActivity, PooledLambda.__(ActivityRecord.class),
 userId, compareIntentFilters, intent, cls);
 final ActivityRecord r = getActivity(p);
 p.recycle();
 return r;
}

其中第8行就是创建了一个PooledPredicate,在我们调用test方法的时候就 会调用RootWindowContainer::matchesActivity这个方法,这个方法的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private static boolean matchesActivity(ActivityRecord r, int userId,
 boolean compareIntentFilters, Intent intent, ComponentName cls) {
 if (!r.canBeTopRunning() || r.mUserId != userId) return false;

 if (compareIntentFilters) {
 if (r.intent.filterEquals(intent)) {
 return true;
 }
 } else {
 if (r.mActivityComponent.equals(cls)) {
 return true;
 }
 }
 return false;
}

首先检查,对应的ActivityRecord是否可以运行在topTask,是否与我们目标要启动的Activity是同样的用户Id,也就是在同一个进程。如果compareIntentFilters为true,还是检查他们的intent是否相同,之后会检查是否为同一个Activity类。对于这个有所了解,我们继续看getActivity的代码,它首先是会调用WindowContainer中的这个方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
 ActivityRecord boundary) {
 if (traverseTopToBottom) {
 for (int i = mChildren.size() - 1; i >= 0; --i) {
 final WindowContainer wc = mChildren.get(i);
 if (wc == boundary) return boundary;

 final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
 if (r != null) {
 return r;
 }
 }
 } else {
 ...
 }

 return null;
}

如果单看上面的代码,我们似乎永远都拿不到ActivityRecord,但是呢ActivityRecord也是WindowContainer的子类,在它当中我们也有同名方法,代码如下:

1
2
3
4
5
@Override
ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
 ActivityRecord boundary) {
 return callback.test(this) ? this : null;
}

这里可以看到,他就是调用了我们刚刚传入的那个PooledPredicate来测试自己是否符合要求,从而我们可以拿到对应的ActivityRecord

计算目标Task: computeTargetTask

到这里我们可以继续看startActivityInner方法中的如下代码:

1
2
3
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
mTargetTask = targetTask;

如果我们刚刚已经拿到reusedTask,那么目标的task就会使用它,如果拿不到则会调用computeTargetTask去获取Task,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private Task computeTargetTask() {
 if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
 && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
 // 同时满足这些条件的情况,不复用task,直接返回空
 return null;
 } else if (mSourceRecord != null) {
 //调用源ActivityRecord,直接复用调用源的Task
 return mSourceRecord.getTask();
 } else if (mInTask != null) {
 //inTask一般是AppTaskImpl指定的,就直接用它,它有可能还没创建这里去创建
 if (!mInTask.isAttached()) {
 getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions);
 }
 return mInTask;
 } else {
 //获取或者创建Task
 final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
 mOptions);
 final ActivityRecord top = rootTask.getTopNonFinishingActivity();
 if (top != null) {
 return top.getTask();
 } else {
 rootTask.removeIfPossible("computeTargetTask");
 }
 }
 return null;
}

这里我们继续去看一下getOrCreateRootTask,代码如下:

1
2
3
4
5
6
7
8
private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
 ActivityOptions aOptions) {
 final boolean onTop =
 (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
 final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;
 return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,
 mLaunchParams, launchFlags);
}

这里还是先拿到调用端的sourceTask以及是否需要onTop,之后调用了RootWindowContainergetOrCreateRootTask方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Task getOrCreateRootTask(@Nullable ActivityRecord r,
 @Nullable ActivityOptions options, @Nullable Task candidateTask,
 @Nullable Task sourceTask, boolean onTop,
 @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
 ...
 TaskDisplayArea taskDisplayArea = null;

 final int activityType = resolveActivityType(r, options, candidateTask);
 Task rootTask = null;
 ...
 int windowingMode = launchParams != null ? launchParams.mWindowingMode
 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 ....
 if (taskDisplayArea == null) {
 taskDisplayArea = getDefaultTaskDisplayArea();
 }
 return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
 launchParams, launchFlags, activityType, onTop);
}

因为我们没有设置什么参数,因此会执行到最后的fallback流程,我们只分析这一部分。默认我们拿到的activityTypeActivity_TYPE_STANDARDgetDefaultTaskDisplayArea会拿到默认的TaskDisplayArea这个之前已经分析过了,最后就是通过它去调用getOrCreateRootTask,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
 @Nullable Task candidateTask, @Nullable Task sourceTask,
 @Nullable ActivityOptions options, int launchFlags) {
 final int resolvedWindowingMode =
 windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode;
 if (!alwaysCreateRootTask(resolvedWindowingMode, activityType)) {
 Task rootTask = getRootTask(resolvedWindowingMode, activityType);
 if (rootTask != null) {
 return rootTask;
 }
 } else if (candidateTask != null) {
 ....
 }
 return new Task.Builder(mAtmService)
 .setWindowingMode(windowingMode)
 .setActivityType(activityType)
 .setOnTop(onTop)
 .setParent(this)
 .setSourceTask(sourceTask)
 .setActivityOptions(options)
 .setLaunchFlags(launchFlags)
 .build();
}

因为我们传进来的windowingModeWINDOWING_MODE_UNDEFINED,因此这里会调用getWindowingMode来设置Mode,这里就是调用系统设置了,不需要看代码。

因为ActivityType是ACTIVITY_TYPE_STAND,所以这里alwaysCreateRootTask为true,因为我们传进来的candidateTask也是空,因此最后就是会创建一个新的Task。但是因为是创建的新task,这个Task里面没有运行中的Activity,因此computeTargetTask还是会返回空。

获取PriorAboveTask和task回收检查

继续回来看startActivityInner内部的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if (targetTask != null) { //在DisplayArea中获取在targetTask Root上面的其他root task
 mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask());
}
//如果newTask为false,则看看目标task 顶部的未finish的ActivityRecord
final ActivityRecord targetTaskTop = newTask
 ? null : targetTask.getTopNonFinishingActivity();
if (targetTaskTop != null) {
 startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
 if (startResult != START_SUCCESS) {
 return startResult;
 }
} else {
 mAddingToTask = true;
}

在可以复用栈的情况下,targetTaskTop不为空,比如singleTask的模式,这个时候会去执行recycleTask。其他情况设置mAddingToTask,表示我们的ActivityRecord需要添加到Task。

1
2
3
4
5
6
7
final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
if (topRootTask != null) {
 startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants);
 if (startResult != START_SUCCESS) {
 return startResult;
 }
}

如果我们检查topRootTask不为空的情况,这里如果我们的启动模式是singleTask,首先会检查task栈顶未启动的Activity是否与当前要启动的相同,如果相同,则不启动当前Activity,仅仅去执行它的newIntent,具体代码就不分析了。

创建RootTask,处理新Activity的Task

再往后看代码,之后就该创建RootTask了,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
if (mTargetRootTask == null) {
 mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
 mOptions);
}
if (newTask) {
 final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
 ? mSourceRecord.getTask() : null;
 setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
 addOrReparentStartingActivity(targetTask, "adding to task");
}

上面调用了getOrCreateRootTask,来创建了新的RootTask,与我们之前分析的类似。同时因为我们之前没有成功创建targetTask,因此这里会执行到setNewTask,而taskToAffiliate没有特殊参数,默认我们先按照空来对待吧。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private void setNewTask(Task taskToAffiliate) {
 final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
 final Task task = mTargetRootTask.reuseOrCreateTask(
 mStartActivity.info, mIntent, mVoiceSession,
 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
 task.mTransitionController.collectExistenceChange(task);
 //把新的ActivityRecord放置到Task列表的顶部
 addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");
 if (taskToAffiliate != null) {
 mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
 }
}

这里大多数情况,toTop会是true,我们去看一下这个reuseOrCreateTask方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
 IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
 ActivityRecord source, ActivityOptions options) {

 Task task;
 if (canReuseAsLeafTask()) { //如果没有Task子节点或者不是组织创建的
 task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
 } else {
 // 创建taskId
 final int taskId = activity != null
 ? mTaskSupervisor.getNextTaskIdForUser(activity.mUserId)
 : mTaskSupervisor.getNextTaskIdForUser();
 final int activityType = getActivityType();
 //创建task,并且当前Task设置为这个Task的Parent,在build当中,把当前的Task放置到Parent的mChildren当中,根据toTop决定是否放置到顶部
 task = new Task.Builder(mAtmService)
 .setTaskId(taskId)
 .setActivityType(activityType != ACTIVITY_TYPE_UNDEFINED ? activityType
 : ACTIVITY_TYPE_STANDARD)
 .setActivityInfo(info)
 .setActivityOptions(options)
 .setIntent(intent)
 .setVoiceSession(voiceSession)
 .setVoiceInteractor(voiceInteractor)
 .setOnTop(toTop)
 .setParent(this)
 .build();
 }

 int displayId = getDisplayId();
 if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
 final boolean isLockscreenShown = mAtmService.mTaskSupervisor.getKeyguardController()
 .isKeyguardOrAodShowing(displayId);
 if (!mTaskSupervisor.getLaunchParamsController()
 .layoutTask(task, info.windowLayout, activity, source, options)
 && !getRequestedOverrideBounds().isEmpty()
 && task.isResizeable() && !isLockscreenShown) {
 //设置task的布局边界
 task.setBounds(getRequestedOverrideBounds());
 }

 return task;
}

上面代码我们就可以复用或者创建新的task,详见注释。拿到Task,或者我们之前已经有Task的情况下(mAddingToTask为true)的时候,还需要执行addOrReparentStartingActivity,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
 TaskFragment newParent = task;
 if (mInTaskFragment != null) {
 //我们的场景不涉及InTaskFragment不为空,忽略
 ...
 } else {
 //当clearTop的时候,并且是可嵌入的,这个时候会保存TaskFragment到mAddingToTaskFragment
 TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
 if (candidateTf == null) {
 //获取目标Task的topRunningActivity,新建的Task不存在
 final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */,
 false /* includingEmbeddedTask */);
 if (top != null) {
 candidateTf = top.getTaskFragment();
 }
 }
 if (candidateTf != null && candidateTf.isEmbedded()
 && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
 //如果拿到了topTask,并且对应的Task是可嵌入的,并且要打开的ActivityRecord也可被嵌入,这把拿到的这个Task作为新的父Task
 newParent = candidateTf;
 }
 }
 //新的ActivityRecord的TaskFragment为空,或者和新的Parent一样,就把这个ActivityRecord放到Task的顶部
 if (mStartActivity.getTaskFragment() == null
 || mStartActivity.getTaskFragment() == newParent) {
 newParent.addChild(mStartActivity, POSITION_TOP);
 } else {
 mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
 }
}

这里会检查如果新的父Task和我们可以复用的Task是否相同,如果相同,或者ActivityRecord中还没有parent,这个时候就把ActivityRecord添加到Task的孩子列表的顶部。而如果ActivityRecord已经存在了parent并且不是我们将要设置的这个,就需要做reparent,这个步骤代码比较复杂,前面调用检查判断的调用省略,直接看最后的调用,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//WindowContainer.java
void reparent(WindowContainer newParent, int position) {
 final DisplayContent prevDc = oldParent.getDisplayContent();
 final DisplayContent dc = newParent.getDisplayContent();

 mReparenting = true;
 //从旧的parent中移除自己,并把自己添加到新parent的指定位置
 oldParent.removeChild(this);
 newParent.addChild(this, position);
 mReparenting = false;

 // 重新布局layout
 dc.setLayoutNeeded();
 //如果新旧的DisplayContent不同,还需要做displayChange的处理
 if (prevDc != dc) {
 onDisplayChanged(dc);
 prevDc.setLayoutNeeded();
 }
 getDisplayContent().layoutAndAssignWindowLayersIfNeeded();

 onParentChanged(newParent, oldParent);
 onSyncReparent(oldParent, newParent);
}

以上的代码我们看到有做parent的替换,但是复杂点在后面的onParentChanged里面,这里会做SurfaceControl的创建或者reparent,这里就不深入了。除此之外,这里还涉及到动画的处理,我们这里也 不深入了。

继续往后看

1
2
3
4
if (!mAvoidMoveToFront && mDoResume) {
 mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
 ...
}

这里会在检查我们的TargetRootTask相比与它的RootTask如果不是在顶部的,需要把它移动到顶部。再往后面就是调用TargetRootTask去启动Activity,以及确认Activity显示出来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
final boolean isTaskSwitch = startedTask != prevTopTask && !startedTask.isEmbedded();
//启动Activity
mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
 mOptions, sourceRecord);
if (mDoResume) {
 final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
 if (!mTargetRootTask.isTopActivityFocusable()
 || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
 && mStartActivity != topTaskActivity)) {
 //如果当前要启动的Activity还没有启动,没有在栈顶端,执行下面的代码
 mTargetRootTask.ensureActivitiesVisible(null /* starting */,
 0 /* configChanges */, !PRESERVE_WINDOWS);
 mTargetRootTask.mDisplayContent.executeAppTransition();
 } else {
 if (mTargetRootTask.isTopActivityFocusable()
 && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
 mTargetRootTask.moveToFront("startActivityInner");
 }
 mRootWindowContainer.resumeFocusedTasksTopActivities(
 mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
 }
}
mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask); //更新用户的rootTask

// 更新系统的最近任务
mSupervisor.mRecentTasks.add(startedTask);

到此位置才算是完成了所有Task计算以及Activity的启动。

通过Adb shell看Activity Task栈

前面都是在解读Android的源码可能比较抽象,其中涉及到了挺多WindowContainer和Task等等相关的查找创建的,为了更加形象。我写了个小demo,主页面是普通的launchMode,另外一次打开了一个singleTask启动Mode的和一个singleInstance 启动Mode的页面,然后我们用一下命令进行Activity Task的dump:

1
adb shell dumpsys activity activities > ~/activitytasks.txt

我们就得到了如下的内容(为方便解读,做了删减):

Display #0 (activities from top to bottom):
* Task{8ed7532 #40 type=standard A=10116:com.example.myapplication U=0 visible=true visibleRequested=true mode=fullscreen translucent=false sz=1}
topResumedActivity=ActivityRecord{3653c00 u0 com.example.myapplication/.SimpleInstanceActivity} t40}
* Hist #0: ActivityRecord{3653c00 u0 com.example.myapplication/.SimpleInstanceActivity} t40}
* Task{ac77886 #39 type=standard A=10116:com.example.myapplication U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=2}
mLastPausedActivity: ActivityRecord{6c019a5 u0 com.example.myapplication/.SingleTaskActivity} t39}
* Hist #1: ActivityRecord{6c019a5 u0 com.example.myapplication/.SingleTaskActivity} t39}
* Hist #0: ActivityRecord{ef92174 u0 com.example.myapplication/.MainActivity} t39}
* Task{d8527c1 #1 type=home U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}
* Task{d60ff49 #33 type=home I=com.android.launcher3/.uioverrides.QuickstepLauncher U=0 rootTaskId=1 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}
mLastPausedActivity: ActivityRecord{868b56f u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t33}
* Hist #0: ActivityRecord{868b56f u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t33}
* Task{2c52978 #36 type=standard A=10044:com.android.documentsui U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}
mLastPausedActivity: ActivityRecord{f9c48b6 u0 com.android.documentsui/.files.FilesActivity} t36}
mLastNonFullscreenBounds=Rect(338, 718 - 1103, 2158)
isSleeping=false
* Hist #0: ActivityRecord{f9c48b6 u0 com.android.documentsui/.files.FilesActivity} t36}
* Task{e38c1d6 #35 type=standard A=10044:com.android.documentsui U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}
mLastPausedActivity: ActivityRecord{32a5344 u0 com.android.documentsui/.files.FilesActivity} t35}
mLastNonFullscreenBounds=Rect(338, 718 - 1103, 2158)
isSleeping=false
* Hist #0: ActivityRecord{32a5344 u0 com.android.documentsui/.files.FilesActivity} t35}
* Task{1d65c74 #3 type=undefined U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=2}
mCreatedByOrganizer=true
* Task{41cb5e3 #5 type=undefined U=0 rootTaskId=3 visible=false visibleRequested=false mode=multi-window translucent=true sz=0}
mBounds=Rect(0, 2960 - 1440, 4440)
mCreatedByOrganizer=true
isSleeping=false
* Task{1cdca12 #4 type=undefined U=0 rootTaskId=3 visible=false visibleRequested=false mode=multi-window translucent=true sz=0}
mCreatedByOrganizer=true
isSleeping=false

这上面就是我们的mRootContainer它当中的的display下面的所有的Task记录,因为我的手机只有一块屏幕,这里只有一个display0, 并且展示了他们的存储关系,这里我们可以看到我们的SimpleInstanceActivity它是在独立的Task当中的。用图表简单描绘一下,结构如下所示:

总结

以上就是Activity Task管理的分析,因为这个流程真的是非常复杂,因此中间的很多步骤还是进行了部分省略。Android系统迭代了这么多年,作为UI展示的组件,Activity承载了太多东西,多屏幕,折叠屏什么的都要支持,因此引入的东西就越来越多。官方也是意识到了这一块的,Activity的管理从AMS抽出来单独的ATMS,ActivityTaskSupervisor的功能也在慢慢抽离到其他的代码中,当前代码里面也添加了很多注释,只要花时间还是能够给搞明白的。

本文仅为一家之言,因为个人疏忽,可能文中也会出现一些错误,欢迎大家指正。

看完评论一下吧

Android源码分析:Activity启动流程分析

2024年9月11日 22:20

Activity是Android中四大组件使用最多的一种,不准确的说,一个Activity就是一个独立页面的承载,因此看Android系统的源码,Activity的启动也是必须要去阅读的。今天的文章就来介绍Activity的启动。因为之前的文章已经分析了ClientTransaction,因此我们对于AMS调用Activity的生命周期和启动有所了解。并且我们也已经分析过了Binder,对于跨进程通讯我们也比较清楚了,不需要细看。我们也分析了应用进程的启动,我们分析Activity启动过程,就不需要去关注应用进程的启动了。有了这些基础,分析Activity的启动会容易一点点。

发起启动Activity

我们首先来看一下启动Activity的调用,我们通常会使用下面的显示调用来启动一个Activity。

1
2
Intent intent = new Intent(context, AActivity.class);
startActivity(intent);

当然也有可能会使用隐式调用来启动一个Activity,如下:

1
2
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://isming.me"));
startActivity(intent);

不过以上两者在启动过程中,仅仅是查找目标组件有区别,并且对于隐式调用,可能存在多个可以启动的Activity,这个时候需要让用户选择目标的页面。对于这一块,我们在后面这个地方会考虑部分略过。

Activity中最终会走到 startActivityForResult(intent, requestCode, options)方法中,这里传入的options我们可以用它设置一些东西,比如App跳转的动画等,我们前面的场景的options为空,并且手机上默认的parent activity也为空,因此会执行这一部分:

1
2
3
4
5
6
7
8
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
 this, mMainThread.getApplicationThread(), mToken, this,
 intent, requestCode, options);
if (ar != null) {
 mMainThread.sendActivityResult(
 mToken, mEmbeddedID, requestCode, ar.getResultCode(),
 ar.getResultData());
}

发起端的处理

在Instrumentation.execStartActivity中会调用如下代码:

1
2
3
4
5
6
int result = ActivityTaskManager.getService().startActivity(whoThread,
 who.getOpPackageName(), who.getAttributionTag(), intent,
 intent.resolveTypeIfNeeded(who.getContentResolver()), token,
 target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
 notifyStartActivityResult(result, options);
 checkStartActivityResult(result, intent);

早期是直接通过ActivityManagerService去启动新的页面的,在这个commit开始把Activity管理的拆分到ActivityTaskManagerService中去。这里我们看到是去获取ActivityTaskManagerService后面简称ATMS,获取ATMS的代码就不罗列了。

这里传到ATMS的参数,包括,发起应用的ApplicationThread,包名(对于普通应用来说opPackage和packageName是一样的),启动的Intent,token和target一般都是空。

ATMS执行startActivity

最终的执行实际是通过binder调用到ActivityTaskManagerService中的startActivity方法,这个方法中又直接调用了startActivityAsUser,其中会有一些检查,检查调用端的uid和packageName是否匹配和其他一些检查,这里不太关注,我们主要关注以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
 Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
 return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
 .setCaller(caller)
 .setCallingPackage(callingPackage)
 .setCallingFeatureId(callingFeatureId)
 .setResolvedType(resolvedType)
 .setResultTo(resultTo)
 .setResultWho(resultWho)
 .setRequestCode(requestCode)
 .setStartFlags(startFlags)
 .setProfilerInfo(profilerInfo)
 .setActivityOptions(opts)
 .setUserId(userId)
 .execute();

从上面的逻辑可以看到,控制Activity启动的代码都放到ActivityStartController中了,首先是获取用户uid,因为每个应用的都会有一个uid,其后就是获取一个ActivityStarter,再通过构建者模式把启动Activity的参数都传到ActivityStarter中去,最后在ActivityStarter的execute()方法中去执行启动的逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
...
if (mRequest.activityInfo == null) { //如果还没有activitgyInfo去填充
 mRequest.resolveActivity(mSupervisor);
}
...
synchronized (mService.mGlobalLock) {
 res = resolveToHeavyWeightSwitcherIfNeeded(); //检查是否为heavy-weight 进程,系统会限制同一时间只有一个heavy-weight进程
 if (res != START_SUCCESS) {
 return res;
 }
 res = executeRequest(mRequest);
}

其中第2行代码就是根据我们的Intent去查询我们将要打开的目标Activity信息。

解析ActivityInfo

resolveActivity中的核心代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//ActivityStarter.Request
void resolveActivity(ActivityTaskSupervisor supervisor) {
 resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
 0 /* matchFlags */,
 computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
 activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
 profilerInfo);
 if (activityInfo != null) {
 intentGrants = supervisor.mService.mUgmInternal.checkGrantUriPermissionFromIntent(
 intent, resolvedCallingUid, activityInfo.applicationInfo.packageName,
 UserHandle.getUserId(activityInfo.applicationInfo.uid));
 }
}

resolveActivity的工作主要由ActivityTaskSupervisor来完成,首先是resolveIntent来获取ResolveInfo,之后调用resolveActivity获取ActivityInfo,最后再去对Intent中的data Uri做权限检查,我们这里只需要分析前两步骤就可。

resolveIntent方法内部,我们看到是调用了PackageManagerServiceresolveIntent方法,代码如下,具体就不深入探究了。

1
2
3
4
//ActivityTaskSupervisor
return mService.getPackageManagerInternalLocked().resolveIntent(
 intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
 filterCallingUid);

resolveActivity代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
 ProfilerInfo profilerInfo) {
 final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
 if (aInfo != null) {
 intent.setComponent(new ComponentName(
 aInfo.applicationInfo.packageName, aInfo.name));
 ...
 }
 return aInfo;
}

这里所做的事情则比较简单,就是从前面拿到的ResolveInfo中拿到activityInfo,并且构建一个ComponentName放到Intent中去。到此为止就拿到了要打开的Activity信息。

ActivityStarter.executeRequest

在前面拿到ActivityInfo,并且我们还构建了一个Request,我们就会继续调用executeRequest方法,其中是有大段的代码 是检查权限,以及一些系统Activity逻辑的处理,不是我们流程关注的重点,重要的是以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
 callingFeatureId);
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
 callingPid, callingUid, checkedOptions)) { //拦截器对要启动的Activity做预处理
 intent = mInterceptor.mIntent;
 rInfo = mInterceptor.mRInfo;
 aInfo = mInterceptor.mAInfo;
 resolvedType = mInterceptor.mResolvedType;
 inTask = mInterceptor.mInTask;
 callingPid = mInterceptor.mCallingPid;
 callingUid = mInterceptor.mCallingUid;
 checkedOptions = mInterceptor.mActivityOptions;

 intentGrants = null;
}

final ActivityRecord r = new ActivityRecord.Builder(mService) //构建ActivityRecord
 .setCaller(callerApp)
 .setLaunchedFromPid(callingPid)
 .setLaunchedFromUid(callingUid)
 .setLaunchedFromPackage(callingPackage)
 .setLaunchedFromFeature(callingFeatureId)
 .setIntent(intent)
 .setResolvedType(resolvedType)
 .setActivityInfo(aInfo)
 .setConfiguration(mService.getGlobalConfiguration())
 ...
 .build();

 mLastStartActivityRecord = r; //保存构建的ActivityRecord

 ...

 mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
 request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
 inTask, inTaskFragment, balCode, intentGrants); //执行启动Activity,并保存结果到mLastStartActivityResult中,以及结果中返回这个result

 if (request.outActivity != null) {
 request.outActivity[0] = mLastStartActivityRecord;
 }
 ...

这里我们有一些权限检查和系统处理之类的没有贴,不过还是贴了一下intercept方法,这里就是给了系统的其他代码来修改Intent的机会。之后就会利用我们传进来的信息去创建ActivityRecord,并且调用startActivityUnchecked去进入下一步:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,...,NeededUriGrants intentGrants) {
 int result = START_CANCELED;
 final Task startedActivityRootTask;

 ......
 try {
 ......
 try {
 .....
 result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
 startFlags, doResume, options, inTask, inTaskFragment, balCode,
 intentGrants);
 } finally {
 startedActivityRootTask = handleStartResult(r, options, result, newTransition,
 remoteTransition); //处理启动Activity的结果
 }
 } finally {
 mService.continueWindowLayout(); //wms处理
 }
 postStartActivityProcessing(r, result, startedActivityRootTask);

 return result;
 }

这里又走到了startActivityInner(),startActivityInner()会去计算launch falgs,去判断是否开创建新的Task还是可以复用task,以及调用启动的后续代码,这个方法的代码比较长我们先一点一点的看。

Task的处理

首先来看其中关于flag的处理,首先就是其中调用的computeLaunchingTaskFlags方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private void computeLaunchingTaskFlags() {
 ...
 if (mInTask == null) {
 if (mSourceRecord == null) {
 if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 }
 } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
 mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
 }
 }

 if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
 && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
 mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
 }
}

这里就是对于我们的启动的LaunchFlag做处理,比如说LAUNCH_SIGLE_INSTANCELAUNCH_SINGLE_TASK都给添加FLAG_ACTIVITY_NEW_TASK等。

随后则是计算Task:

1
2
3
4
5
6
7
final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
final Task reusedTask = getReusableTask();

final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
mTargetTask = targetTask;

getFocusedRootTask会尝试去获取首选的Task,如果不存在也会从当前显示屏获取获取最顶部的可触摸并且在展示的Task。而这个preTopTask如果能够获取到,它又会去获取的它叶子节点。叶子节点的规则就是没有只节点。Task相关类的继承结果如下:

classDiagram
class ConfigurationContainer {
<<abstract>>
}
class WindowContainer
class TaskFragment
class Task
ConfigurationContainer <|--WindowContainer
WindowContainer <|-- TaskFragment
TaskFragment <|--Task
WindowContainer <|--RootWindowContainer
1
2
3
4
if (mTargetRootTask == null) {
 mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
 mOptions);
}

这里最终会拿到RootTask,如果没有也会创建,具体代码这里不分析了。

调用Task的 resumeFocusedTasksTopActivities

之后会调用如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ActivityStarter
 ...
 mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
 mOptions, sourceRecord); //这个名字是startActivityLock但并不是真的打开activity,而是把Activity对应的task放到列表的最前面,以及会展示window动画
 if (mDoResume) {
 if (!mTargetRootTask.isTopActivityFocusable()
 || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
 && mStartActivity != topTaskActivity)) {
 //对样式pip页面或者其他一些情况的处理
 ...
 } else {
 ...
 //真正的启动Activity的代码这里是入口
 mRootWindowContainer.resumeFocusedTasksTopActivities(
 mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
 }
 ...

 }
 ...

 return START_SUCCESS;
 }

我们看到首先调用了startActivityLocked方法,这里主要做的就是把我们的ActivityReccord放到Task中去,并且展示Activity的启动动画。之后调用的RootContainerresumeFocsedTasksTopActivities才是真正的启动,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//RootWindowContainer.java
boolean resumeFocusedTasksTopActivities(Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
boolean deferPause) {
 ...
 boolean result = false;
 if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
 || getTopDisplayFocusedRootTask() == targetRootTask)) {
 result = targetRootTask.resumeTopActivityUncheckedLocked(
 target,targetOptions, deferPause); //执行启动
 }
 ...
}

后面会走到Task的resumeTopActivityUnCheckedLocked方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,
 boolean deferPause) {
 ...
 if (isLeafTask()) {
 if (isFocusableAndVisible()) { //可触摸可见
 someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
 }
 } else {
 int idx = mChildren.size() - 1;
 while (idx >= 0) {
 final Task child = (Task) getChildAt(idx--);
 if (!child.isTopActivityFocusable()) {
 continue;
 }
 if (child.getVisibility(null /* starting */)
 != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
 if (child.topRunningActivity() == null) {
 continue;
 }
 break;
 }

 someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,
 deferPause);
 if (idx >= mChildren.size()) {
 idx = mChildren.size() - 1;
 }
 }
 }


}

此处如果当前的Task本来就是叶子节点,那么会调用resumeTopActivityInnerLocked方法,否则会遍历子的task列表,在子task列表中找到符合条件的去执行resumeTopActivityUncheckedLocked方法,如此最后还是会调用到resumeTopActivityInnerLocked方法,而我们再跟进去看,可以看到其中的核心逻辑是调用topFragment的resumeTopActivity方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
 boolean deferPause) {
 ...
 boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
 if (mResumedActivity != null) {
 pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
 next, "resumeTopActivity"); //首先把顶部处于Resumed状态的activity执行pausing
}
if (pausing) {
 //检查即将启动的Activity的Activity的进程有没有起来,如果没有进程去创建进程,创建进程的代码需要单独分析,此处略过
 if (next.attachedToProcess()) {
 next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
 true /* activityChange */, false /* updateOomAdj */,
 false /* addPendingTopUid */);
 } else if (!next.isProcessRunning()) {
 final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
 mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
 isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY
 : HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);
 }
 ...
 return true;
 }
 if (next.attachedToProcess()) { //如何Activity已经在这个进程中了
 ...
 final ClientTransaction transaction =
 ClientTransaction.obtain(next.app.getThread(), next.token); //构建ClientTransaction,传入要打开的Activity对应的applicationThread和IBinder
 ...
 if (next.newIntents != null) { //把intent放进去,后面会把Activity吊起,并 调用onNewIntent()
 transaction.addCallback(
 NewIntentItem.obtain(next.newIntents, true /* resume */));
 }
 ...
 mAtmService.getLifecycleManager().scheduleTransaction(transaction);

 } else {
 ...
 mTaskSupervisor.startSpecificActivity(next, true, true); //启动Activity
 }


}

上面最后的代码可以看到,Activity已经存在的时候是走到onNewIntent, 调用的代码被包装成了ClientTransaction,通过ClientlifecycleManager 的scheduleTransaction方法,最终其实是调用了IApplicationThread的scheduleTransaction,最终通过binder调用到了app进程中的同名方法,这里要去看ActivityThread, ApplicationThread为它的内部类,看它的代码,它实际调用了ActivityThread的同名方法。而启动Activity,我们一路跟着startSpecificActivity()方法进去最终会看到也是通过ClientTransaction,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
 System.identityHashCode(r), r.info,
 // TODO: Have this take the merged configuration instead of separate global 
 // and override configs. 
 mergedConfiguration.getGlobalConfiguration(),
 mergedConfiguration.getOverrideConfiguration(), r.compat,
 r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
 proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
 results, newIntents, r.takeOptions(), isTransitionForward,
 proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
 r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));
// Set desired final state. 
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
 lifecycleItem = ResumeActivityItem.obtain(isTransitionForward,
 r.shouldSendCompatFakeFocus());
} else {
 lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
mService.getLifecycleManager().scheduleTransaction(clientTransaction);

目标进程执行performLaunchActivity

我们之前已经分析过ClientTransaction,我们知道这个LaunchActivityItem的callback,最后client就是我们的ActivityThread,会执行它的handleLaunchActivity方法,其中最核心的就是如下这一句:

1
final Activity a = performLaunchActivity(r, customIntent);

我们继续往里面看:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//content of performLaunchActivity() function
...
//为Activity创建Base Context
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
...
//创建Activity实例,就是通过反射来实例化一个Activity实例
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
 cl, component.getClassName(), r.intent);
...
//拿到Application的实例,如果缓存中有就用,没有就创建一个新的,这里也不看具体代码了
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
...
//为Activity创建配置,如里面有语言,屏幕设置等等参数,不具体分析了
Configuration config =
 new Configuration(mConfigurationController.getCompatConfiguration());
if (r.overrideConfig != null) {
 config.updateFrom(r.overrideConfig);
}
...
//把Activity和baseContext绑定,并且把一些参数附加到Activity实例上去
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
 r.ident, app, r.intent, r.activityInfo, title, r.parent,
 r.embeddedID, r.lastNonConfigurationInstances, config,
 r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
 r.assistToken, r.shareableActivityToken);
...
mInstrumentation.callActivityOnCreate(activity, r.state); //这一步完成,Activity里面会执行完onCreate()
...
r.setState(ON_CREATE);

再来具体看一看Activity的attach方法中做了什么

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
....
mWindow.setWindowManager(
 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
 mToken, mComponent.flattenToString(),
 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;

可以看到在里面绑定了baseContext,以及创建了我们的PhoneWindow,以及把window和windowManager进行绑定,还有其他一些Activity内部会用到的参数的传递。

而mInstrumentation里面最终是会调用Activity的performCreate,其中则会调用activity的onCreate。这里就不贴相关代码了。

这样我们的Activity才走完onCreate,而剩余步骤,我们之前还设置了LifecycleStateRequestResumeActivityItem,因此这是要让我们的Activity最终进入到Resume状态,具体的可以参看ClientTransaction分析。两篇文章配合着一起,就是完整的Activity启动流程了。

总结

从Activity调用startActivity,一直到A T M S调用ActivityStarter的调用时序图如下:

sequenceDiagram
Activity->>Activity: startActivity
Activity->>Instrumentation:execStartActivity
Instrumentation->>ATMS: startActivity
ATMS->>ActivityStarter: execute

从ActivityStarter调用到新的进程处理的时序图如下(省略了到Activity部分的流程):

sequenceDiagram
ActivityStarter->>ActivityStarter: resolveActivity
ActivityStarter->>ActivityStarter: executeRequest
ActivityStarter->>ActivityStarter: startActivityUnchecked
ActivityStarter->>RootWindowContainer: resumeFocusedTasksTopActivities
RootWindowContainer->>Task: resumeTopActivityUncheckedLocked
Task->>Task: resumeTopActivity
Task->>ActivityTaskSupervisor: startSpecificActivity
ActivityTaskSupervisor->>ActivityTaskSupervisor: realStartActivityLocked
ActivityTaskSupervisor->>ActivityThread: handleLaunchActivity
note right of ActivityTaskSupervisor: 通过ClientTransaction
ActivityThread->>ActivityThread: performLaunchActivity

以上就是一个较为精简的Activity启动的流程。其中省略了不少东西,关于startActivityForResult的情况需要获取到打开的Activity的结果的情况这里还没有讨论。

看代码可以发现Activity的启动过程是非常的复杂的,再加上新版本的Android支持多屏幕,折叠屏,分屏,画中画等等非常多的特性,因而Task的复用,新建就很复杂,因此本文这一部分暂时放下,等到以后在写。

如果你也对于Android系统源码感兴趣,欢迎与我交流。博文因为个人局限,也难免会出现差错,欢迎大家指正。

看完评论一下吧

Android源码分析: 应用进程启动分析

2024年9月9日 20:11

Android应用进程的启动,简单来说就是从zygot进程fork出来一个新进程,并对其进行一些初始化。这样做系统的一些代码和资源等等就不需要重复加载,一些环境变量也都不需要重新设置,可以说是很巧妙的设置。下面就来具体分析一下其初始化过程。

启动时机

应用进程的启动,一般是在创建四大组件,比如说启动Activity,Service,使用ContentProvider,有广播需要处理,这些情况需要创建进程。在我们分析的代码当中,除了这几种情况,BackupAngent也会涉及到创建App进程。

启动进程调用的为AMS当中的startProcessLocked方法, 我们注意看的话,AMS当中还有另一个方法startIsolatedProcess也是用来启动进程的,但是这个方法它启动的进程一般是给系统使用的,我们这里不会分析。

AMS调用启动进程

我们就从AMSstartProcessLocked这个方法开始看起来:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
final ProcessRecord startProcessLocked(String processName,
 ApplicationInfo info, boolean knownToBeDead, int intentFlags,
 HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
 boolean isolated) {
 return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
 hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
 false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,
 null /* sdkSandboxClientAppPackage */,
 null /* ABI override */, null /* entryPoint */,
 null /* entryPointArgs */, null /* crashHandler */);
}

这里我们传入的参数intentFlags为0,zygotePolicyFlagsZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, allowWhileBootingfalse, isolatedfalse。之后startProcessLocked方法内部用调用了ProcessList的同名方法,其中我们关注的核心语句如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ProcessRecord app;
...
app = getProcessRecordLocked(processName, info.uid); //从缓存中获取ProcessRecord
...
if (app == null) {
 app = newProcessRecordLocked(info, processName, isolated, isolatedUid, isSdkSandbox,
 sdkSandboxUid, sdkSandboxClientAppPackage, hostingRecord);
} else {
 app.addPackage(info.packageName, info.longVersionCode, mService.mProcessStats);
}
...
final boolean success =
 startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);

以上代码可以看到,会先去获取是否有现有的processRecord可用,有的话就拿出来使用,没有的话会创建新的,之后会调用startProcessLocked方法。ProcessList中使用mProcessNames来存储ProcessRecord与processName和uid的对应关系,查找的逻辑就是从map中查找不再关注。newProcessRecordLocked方法则是创建新的ProcessRecord,并且会把各种信息保存到这个record当中去。我们这里可以继续看startProcessLocked方法,,最终会调用这个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
 int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
 String abiOverride) {
 //对于从缓存拿到的ProcessRecord,把原来的信息清掉
 if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
 mService.removePidLocked(app.getPid(), app);
 app.setBindMountPending(false);
 app.setPid(0);
 app.setStartSeq(0);
 }
 app.unlinkDeathRecipient();
 app.setDyingPid(0);
 ...
 final IPackageManager pm = AppGlobals.getPackageManager();
 permGids pm.getPackageGids(app.info.packageName, =
 MATCH_DIRECT_BOOT_AUTO, app.userId);
 StorageManagerInternal storageManagerInternal = LocalServices.getService(
 StorageManagerInternal.class);
 mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
 app.info.packageName); //检查外部存储访问权限
 externalStorageAccess = storageManagerInternal.hasExternalStorageAccess(uid,
 app.info.packageName);
 if (pm.checkPermission(Manifest.permission.INSTALL_PACKAGES,
 app.info.packageName, userId)
 == PackageManager.PERMISSION_GRANTED) { //检查安装应用的权限
 Slog.i(TAG, app.info.packageName + " is exempt from freezer");
 app.mOptRecord.setFreezeExempt(true);
 }
 if (app.processInfo != null && app.processInfo.deniedPermissions != null) {
 for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) {
 int[] denyGids = mService.mPackageManagerInt.getPermissionGids(
 app.processInfo.deniedPermissions.valueAt(i), app.userId);
 if (denyGids != null) {
 for (int gid : denyGids) {
 permGids = ArrayUtils.removeInt(permGids, gid);
 }
 }
 }
 }

 gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess); //根据前面的权限和相关信息,计算新启动的进程 需要分配的用户
 ...
 //读取app的debuggable,profileable等标志位
 boolean debuggableFlag = (app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 boolean isProfileableByShell = app.info.isProfileableByShell();
 boolean isProfileable = app.info.isProfileable();
 if (debuggableFlag) {
 runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
 runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
 runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;


 if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(), android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {
 runtimeFlags |= Zygote.DISABLE_VERIFIER;
 }
 }
 if (isProfileableByShell) {
 runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
 }
 if (isProfileable) {
 runtimeFlags |= Zygote.PROFILEABLE;
 } //把标志位信息保存到runtimeFlags中
 ...//其他一些flag写入到runtimeFlags中去
 if (debuggableFlag) {
 //debuggable时候使用wrap.sh去fork进程
 String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
 try {
 if (new File(wrapperFileName).exists()) {
 invokeWith = "/system/bin/logwrapper " + wrapperFileName;
 }
 } finally {
 StrictMode.setThreadPolicy(oldPolicy);
 }
 }
 String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
 if (requiredAbi == null) { //设置 app native 库使用的abi,如arm或者x86或者armv8等等
 requiredAbi = Build.SUPPORTED_ABIS[0];
 }
 String instructionSet = null;
 if (app.info.primaryCpuAbi != null) {
 instructionSet = VMRuntime.getInstructionSet(requiredAbi);
 }

 app.setGids(gids);
 app.setRequiredAbi(requiredAbi);
 app.setInstructionSet(instructionSet); //把信息都设置到ProcessRecord中
 final String seInfo = app.info.seInfo
 + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
 final String entryPoint = "android.app.ActivityThread"; //设置进程入口位ActivityThread

 return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
 runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
 instructionSet, invokeWith, startUptime, startElapsedTime);

}

以上代码主要是检查应用的各种权限,对其设置对应权限组的groupId,以及设置应用的Abi等信息。之后又会启动一个新的startProcessLocked方法,其中仍然是给ProcessRecord设置参数,其中很大篇幅的为设置debug和profilable相关的参数设置,这里就不列出参数设置的代码了,只列以下最后启动调用的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
 mService.mProcStartHandler.post(() -> handleProcessStart(
 app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
 requiredAbi, instructionSet, invokeWith, startSeq));
 return true;
} else {
 final Process.ProcessStartResult startResult = startProcess(hostingRecord,
 entryPoint, app,
 uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
 requiredAbi, instructionSet, invokeWith, startUptime);
 handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
 startSeq, false);
 return app.getPid() > 0;
}

这里有两个分支,这个 FLAG_PROCESS_START_ASYNC 默认为True,是通过系统的Setting去设置的。第一个分支是通过Handle把任务抛出去执行,而直接返回了执行成功,另一个分支则是等待任务执行完成,在根据返回的UID检查是否成功。不过两个分支里面都是执行了startProcess方法,在这个方法中我们关注以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (hostingRecord.usesWebviewZygote()) { //webview进程的创建
 startResult = startWebView(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, null, app.info.packageName,
 app.getDisabledCompatChanges(),
 new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

 startResult = appZygote.getProcess().start(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, null, app.info.packageName,
 /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
 app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
 false, false,
 new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
 regularZygote = true;
 startResult = Process.start(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
 isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
 allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
 new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
}

以上可以看到我们在创建新的进程的时候,会有三个分支,我们回看我们创建HostingRecord 时候是调用的如下的构造方法:

1
2
3
4
5
public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) {
 this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
 null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
 null /* definingProcessName */, null /* action */, TRIGGER_TYPE_UNKNOWN);
}

Process启动进程调用

因此上面的代码是走到了regular分支,它调用了Processstart方法, Process中又调用了ZYGOTE_PROCESSstart方法, ZYGOTE_PROCESS为一个ZygoteProcess常量,其中又会调用startViaZygoate方法,我们来看看这个方法的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private Process.ProcessStartResult startViaZygote(@NonNull final String processClass, @Nullable final String niceName, final int uid, final int gid, @Nullable final int[] gids,
 int runtimeFlags, int mountExternal,
 int targetSdkVersion,
 @Nullable String seInfo,
 @NonNull String abi,
 @Nullable String instructionSet,
 @Nullable String appDataDir,
 @Nullable String invokeWith,
 boolean startChildZygote,
 @Nullable String packageName,
 int zygotePolicyFlags,
 boolean isTopApp,
 @Nullable long[] disabledCompatChanges,
 @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap,
 @Nullable Map<String, Pair<String, Long>> allowlistedDataInfoList,
 boolean bindMountAppsData,
 boolean bindMountAppStorageDirs,
 @Nullable String[] extraArgs) throws ZygoteStartFailedEx {
 ArrayList<String> argsForZygote = new ArrayList<>();
 argsForZygote.add("--runtime-args");
 argsForZygote.add("--setuid=" + uid);
 argsForZygote.add("--setgid=" + gid);
 argsForZygote.add("--runtime-flags=" + runtimeFlags);
 ....
 synchronized(mLock) {
 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
 zygotePolicyFlags,
 argsForZygote);
}

上面的代码就是把我们之前所有的各种参数,都拼接起来放到一个字符数组中,后面的openZygoteSocketIfNeeded则是根据abi来于zygote进程建立socket连接,其他的我就要进入zygoteSendArgsAndGetResult方法中查看详情了。

1
2
3
4
5
if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
 return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
}

return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);

这里有一个判断是否要使用usap进程池(非专门app使用进程池),不过我看了这里mUsapPoolEnabled字段默认为false,那我们就不看这个分支了。而attemptZygoteSendArgsAndGetResult代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
 ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
 try {
 final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
 final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

 zygoteWriter.write(msgStr);
 zygoteWriter.flush();

 Process.ProcessStartResult result = new Process.ProcessStartResult();
 result.pid = zygoteInputStream.readInt();
 result.usingWrapper = zygoteInputStream.readBoolean();

 if (result.pid < 0) {
 throw new ZygoteStartFailedEx("fork() failed");
 }

 return result;
 } catch (IOException ex) {
 zygoteState.close();
 Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
 + ex.toString());
 throw new ZygoteStartFailedEx(ex);
 }
}

从上面的代码我们可以看到,这里其实很简单,就是通过socket向Zytgote发送了我们启动进程需要的参数,然后再通过socket从Zygote读出创建的进程的pid。

Zygote进程创建子进程

这个时候我们需要来看ZygoteInit的main方法,具体zygote进程是如何在系统启动的时候创建的就不去关注了,这里来关注zygote进程如何去创建应用进程的,这里摘抄了一些它的main函数的代码:

1
2
3
4
5
6
7
preload(bootTimingsTraceLog); //zygote启动之后,预加载代码资源等
zygoteServer = new ZygoteServer(isPrimaryZygote); //创建Zygote 的socket server
caller = zygoteServer.runSelectLoop(abiList); // socket server进入监听状态

if (caller != null) {
 caller.run(); //子进程中的时候caller不为空,会执行,此处会执行我们的ActivityThread的main方法,先分析上面的runSelectLoop,其中会有caller的创建
}

runSelectLoop内我们比较关注的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Runnable runSelectLoop(String abiList) {
 while (true) {
 pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
 if (pollReturnValue == 0) {
 ...
 } else {
 while (--pollIndex >= 0) {
 if (pollIndex == 0) {
 //如果pollIndex为0,则说明没有socket连接,需要创建socket连接
 ZygoteConnection newPeer = acceptCommandPeer(abiList);
 peers.add(newPeer);
 socketFDs.add(newPeer.getFileDescriptor());
 } else if (pollIndex < usapPoolEventFDIndex) { //读取Primary socket
 ZygoteConnection connection = peers.get(pollIndex);
 boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
 final Runnable command = connection.processCommand(this, multipleForksOK);
 if (mIsForkChild) {
 return command; //子进程,返回command
 } else {
 //父进程的一些处理
 }
 ...
 }
 ....
 }
 ...
 }

 }
}

上面的代码省略了一些如果是Usap进程的代码,代码里面有两层的循环,在内层循环中,以pollIndex作为循环的条件,如果pollIndex为0,在acceptCommandPeer中会建立新的Socket Connet,代码里面就是一个ZygoteConnection。如果存在Connect的情况下会,会通过判断当前pollIndex是否小于usapPollEventFDIndex来判断是否是普通的进程创建,之后会调用connection.processCommand来读取socket数据做后续的处理,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
ZygoteArguments parsedArgs;
try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
 while (true) {
 parsedArgs = ZygoteArguments.getInstance(argBuffer);
 ...
 if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
 || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
 pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
 parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
 parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
 fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
 parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
 parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
 parsedArgs.mBindMountAppStorageDirs); //fork子进程的操作

 try {
 if (pid == 0) { //子进程处理分支
 zygoteServer.setForkChild();

 zygoteServer.closeServerSocket();
 IoUtils.closeQuietly(serverPipeFd);
 serverPipeFd = null;

 return handleChildProc(parsedArgs, childPipeFd,
 parsedArgs.mStartChildZygote);
 } else { //父进程处理分支
 IoUtils.closeQuietly(childPipeFd);
 childPipeFd = null;
 handleParentProc(pid, serverPipeFd);
 return null;
 }
 } finally {
 IoUtils.closeQuietly(childPipeFd);
 IoUtils.closeQuietly(serverPipeFd);
 }
 } else {
 ...
 }
 ...
 }
}

上面代码是处理socket数据的代码,我这里省略了除了创建进程之外的处理其他操作的代码。其中我们可以看到系统是使用了ZygoteArguments来解析我们之前从system_server进程传过来的参数,之后调用Zygote.forkAndSpecialize来创建进程,在linux中,fork完进程之后,是通过pid来判断当前是在父进程还是子进程中的,当前为子进程则pid为0。forkAndSpecialize方法中主要是调用了nativeForkAndSpecialize,这个是native方法,代码在com_android_internal_os_Zygote.cpp中,在native中的方法为com_android_internal_os_Zygote_nativeForkAndSpecialize我们去看看它的代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
...
pid_t pid = zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
 SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
 mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
 instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
 allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
 mount_storage_dirs == JNI_TRUE);
}
return pid;

上面的代码可以看到,第一行是去fork子进程,后面会判断是否为子进程,如果为子进程则会为子进程做一些处理。其中我省略了前面一部分fds_to_close 和fds_to_ignore赋值的代码,那些为需要关闭或者忽略的文件描述符,会传到这个ForkCommon方法中,我们具体看看这个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
SetSignalHandlers(); //设置错误信号监听
BlockSignal(SIGCHLD, fail_fn); //暂时关闭SIGCHLD信号,方便后面关闭fd
__android_log_close(); //关闭log相关的FD
AStatsSocket_close();
...
pid_t pid = fork(); //调用系统调用执行fork进程

if (pid == 0) {
 ...
 PreApplicationInit(); //子进程的初始化,主要是设置当前进程不是zygote进程
 DetachDescriptors(env, fds_to_close, fail_fn); //把传进来的要关闭的fd关掉
 ...
} else {
 ...
}

UnblockSignal(SIGCHLD, fail_fn); //重新打开之前关闭的SIGCHLD信号
return pid;

可以看到上面的代码主要是去调用fork系统调用去从zygote进程fork一个新进程作为应用使用的进程,而SpecializeCommon,我们根据传入的参数和代码可以知道,其中主要是设置子进程的用户组,以及挂载应用目录,一些其他相关的初始化,就不分析其代码了。然后我们就可以继续会到java代码。

创建完子进程后的操作

在前面processCommand方法中,我们知道fork成功之后如果是子进程会执行handleChildProc方法,如果是父进程会执行handleParentProc方法,先来看一下父进程执行的代码:

1
2
3
4
5
6
if (pid > 0) {
 setChildPgid(pid);
}
...
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);

这个方法中我们需要关注的就上面这一部分代码,首先是把这个子进程的pid放到进程的当前进程的孩子进程组中去。后面的就是把子进程的pid和是否使用了wrapper写入到socket中,这样我们之前请求创建进程那个地方就能拿到子进程的id了。

再来看子进程所执行的handleChildProc方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);

if (parsedArgs.mInvokeWith != null) {
 WrapperInit.execApplication(parsedArgs.mInvokeWith,
 parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
 VMRuntime.getCurrentInstructionSet(),
 pipeFd, parsedArgs.mRemainingArgs);

 // Should not get here. 
 throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
 if (!isZygote) {
 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
 parsedArgs.mDisabledCompatChanges,
 parsedArgs.mRemainingArgs, null /* classLoader */);
 } else {
 return ZygoteInit.childZygoteInit(
 parsedArgs.mRemainingArgs /* classLoader */);
 }
}

首先第一行是关闭socket,前面的native代码其实已经关闭过了socket,但是在java层还是有LocalSocket,也需要关闭。 第二行就是给我们这个进程设置名称。 后面的第一个判断是看我们是否使用wrapper,正常流程不会走到这里,else分支中我们这里也不是fork一个新的zygote进程,因此也只需要看ZygoteInit.zygoteInit这个方法即可。

1
2
3
4
5
6
RuntimeInit.redirectLogStreams(); //关闭默认的log,设置使用android的print来输出system.out和system.error的log

RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
 classLoader);

App进程的初始化

RuntimeInit.commonInit中是一些初始化,包括错误处理,时区,网络的userAgent等,不看代码了。nativeZygoteInit的代码在AndroidRuntime.cpp

1
2
3
4
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
 gCurRuntime->onZygoteInit();
}

此处调用了gCurRuntimeonZygoteInit()方法,而这个方法是AndroidRuntime中的一个虚方法,在app_main.cpp中我们看到实际上对于应用我们有一个子类AppRuntime中实现了这个方法,代码如下:

1
2
3
4
5
6
virtual void onZygoteInit()
{
 sp<ProcessState> proc = ProcessState::self();
 ALOGV("App process: starting thread pool.\n");
 proc->startThreadPool();
}

我们之前分析binder的时候,知道ProcessState这个类binder是有使用的,调用self方法会打开binder驱动,这个代码里面是为binder创建应用进程的线程池,具体这里就不分析了。继续看RuntimeInit.applicationInit代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
 String[] argv, ClassLoader classLoader) {
 nativeSetExitWithoutCleanup(true);

 VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
 VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

 final Arguments args = new Arguments(argv);

 return findStaticMain(args.startClass, args.startArgs, classLoader);
}

上面的方面,前面的代码主要是设置targetSdkversion和其他的一些设置,我们主要来看后面的findStaticMain方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected static Runnable findStaticMain(String className, String[] argv,
 ClassLoader classLoader) {
 Class<?> cl;

 try {
 cl = Class.forName(className, true, classLoader);
 } catch (ClassNotFoundException ex) {
 throw new RuntimeException(
 "Missing class when invoking static main " + className,
 ex);
 }

 Method m;
 try {
 m = cl.getMethod("main", new Class[] { String[].class });
 } catch (NoSuchMethodException ex) {
 throw new RuntimeException(
 "Missing static main on " + className, ex);
 } catch (SecurityException ex) {
 throw new RuntimeException(
 "Problem getting static main on " + className, ex);
 }

 int modifiers = m.getModifiers();
 if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
 throw new RuntimeException(
 "Main method is not public and static on " + className);
 }
 return new MethodAndArgsCaller(m, argv);
}

可以看到我们通过反射拿到应用的之前设置的应用入口,也就是ActivityThread类,之后再获取到它的main方法,最后组装成一个MethodAndArgsCaller对象,最后返回。从前面的代码我们知道它会在ZygoteInitmain方法中执行。然后我们就可以来分析ActivityThread代码了。

ActivityThread代码执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static void main(String[] args) {
 AndroidOs.install();
 Environment.initForCurrentUser();
 final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
 TrustedCertificateStore.setDefaultUserDirectory(configDir);
 initializeMainlineModules();

 Looper.prepareMainLooper();
 ...
 ActivityThread thread = new ActivityThread();
 thread.attach(false, startSeq);
 Looper.loop();
 throw new RuntimeException("Main thread loop unexpectedly exited");

}

上面的代码可以看到是为应用进程做一些初始化,首先是为sys call使用android的一些定制,其次是指定CA证书的位置,之后安装Mainline的模块,后面是初始化looper进入Looper循环,这样应用的主线程也就完成了初始化。在启动loop之前有一个attach方法,对于应用进程我们传进来的第一个参数为false, 也就是非系统进程,我们来看代码,只看应用进程的分支。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
 mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
 throw ex.rethrowFromSystemServer();
}

BinderInternal.addGcWatcher(new Runnable() {
 public void run() {
 //监听gc,当可用内存比较小的时候尝试回收一些Activity
 }
}

ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
 //config 变化的回调,用来更新app得而configuration
}
ViewRootImpl.addConfigCallback(configChangedCallback);

上面的代码主要做了四件事情,其中两个是注册gc的回调和view configration 变化的回调,我们最关注的是调用ActivityManager的atttachApplication,和RuntimeInit的setApplicationObject,它们都用到了mAppThread,这个对象为ApplicationThread,而它是IApplicationThread.aidl的客户端实现。这里首先是把它的IBinder对象传到RuntimeInit中,这样发生一些事情的时候系统可以通知到应用。

回到AMS

另外我们再来看一下ActivityManager的attchApplication方法,它实际调用的是ActivityManagerServiceattachApplication方法,在它内部又调用了attachApplicationLocked方法,这里只看一下我们比较关心的一部分代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode
 ? mCpHelper.generateApplicationProvidersLocked(app)
 : null;
...
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
...
thread.bindApplication(processName, appInfo,
 app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
 providerList, null, profilerInfo, null, null, null, testMode,
 mBinderTransactionTrackingEnabled, enableTrackAllocation,
 isRestrictedBackupMode || !normalMode, app.isPersistent(),
 new Configuration(app.getWindowProcessController().getConfiguration()),
 app.getCompat(), getCommonServicesLocked(app.isolated),
 mCoreSettingsObserver.getCoreSettingsLocked(),
 buildSerial, autofillOptions, contentCaptureOptions,
 app.getDisabledCompatChanges(), serializedSystemFontMap,
 app.getStartElapsedTime(), app.getStartUptime());
...
if (normalMode) {
 try {
 didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
 } catch (Exception e) {
 Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
 badApp = true;
 }
}
if (!badApp) {
 try {
 didSomething |= mServices.attachApplicationLocked(app, processName);
 checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked"); //检查是否有Service需要在当前进程启动
 } catch (Exception e) {
 Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
 badApp = true;
 }
}

if (!badApp && isPendingBroadcastProcessLocked(pid)) {
 try {
 didSomething |= sendPendingBroadcastsLocked(app); //发送pending的广播
 checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
 } catch (Exception e) {
 // If the app died trying to launch the receiver we declare it 'bad' 
 Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
 badApp = true;
 }
}

上面省略了一些代码,不过我们application需要做的一些核心代码都还在。除了列出的代码外,这里其实还有一些pending service启动,pending 广播的执行,以及ContentProvider的安装等,这些我们先略过。 首先这个normalMode的判断,我们假设当前已经是使用中而不是刚启动手机,而mProcessesReady是在system_server启动之后就赋值为true了,所以对于app启动的状况来说,这里normalMode为true。这里我们需要重点关注的就两个地方,一个是thread.bindApplication,另一处是mAtmInternal.attachApplication。bindApplication会通过binder调用到应用进程的bindApplication方法。

1
2
3
4
AppBindData data = new AppBindData();
data.processName = processName;
....
sendMessage(H.BIND_APPLICATION, data);

这里主要就去构建了AppBindData,使用ActivityThread内部的H来发送消息,消息回调处会调用ActivityThread的handleBindApplication方法。这个方法的代码非常多,前面的一些是设置包名,进程名称等等信息,以及configration信息以及调试器相关的东西,这些我们都不关注,这里跳过。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21

data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo, isSdkSandbox); //构建apk信息,创建LoadApk对象。

//创建AppContent,并且把confirmration绑定到Context上
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
mConfigurationController.updateLocaleListFromAppContext(appContext);

//创建Instrumentation
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);

if (!data.restrictedBackupMode) { //执行安装ContentProvider
 if (!ArrayUtils.isEmpty(data.providers)) {
 installContentProviders(app, data.providers);
 }
}

//创建程序的Application
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
//调用Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);

上面的逻辑我们需要关注makeApplicationInner, 后面的callApplicationOnCreate内部就是调用Application的onCreate方法,不再分析了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
final Application cached = sApplications.get(mPackageName);
if (cached != null) {
 if (!allowDuplicateInstances) {
 mApplication = cached;
 return cached;
 }
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
 cl, appClass, appContext);
appContext.setOuterContext(app);

上面的代码主要是从缓存里面取Application,如果没有则通过Instrumentaion去创建新的Applicaion,我们继续去看newApplication的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public Application newApplication(ClassLoader cl, String className, Context context)
 throws InstantiationException, IllegalAccessException,
 ClassNotFoundException {
 String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
 myProcessName);
 Application app = getFactory(context.getPackageName())
 .instantiateApplication(cl, className);
 app.attach(context);
 return app;
}

这里的代码比较简单,就是拿到AppComponentFactory然后通过反射创建App的Application对象,之后调用app的attach方法,attach方法内部会调用attachBaseContext方法。就不往里去看代码了。

对于需要启动Activity的情况,我们需要看ActivityManagerServiceattachApplication,我们需要再看一下mAtmInternal.attachApplication。它会调用ActivityTaskManagerService的内部类LocalService的方法,内部会调用mRootWindowContainer.attachApplication(wpc);,它的内部又会调用mAttachApplicationHelper.process(app),内部又会调用ensureActivitiesVisible方法,一路看进去最终会调用EnsureActivitiesVisibleHelperprocess方法,它的内部会调用setActivityVisibilityState

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
if (!r.attachedToProcess()) {
 makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop,
 resumeTopActivity && isTop, r);
} else if (r.isVisibleRequested()) {
 // If this activity is already visible, then there is nothing to do here. 
 if (DEBUG_VISIBILITY) {
 Slog.v(TAG_VISIBILITY, "Skipping: already visible at " + r);
 }

 if (r.mClientVisibilityDeferred && mNotifyClients) {
 r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting);
 r.mClientVisibilityDeferred = false;
 }

 r.handleAlreadyVisible();
 if (mNotifyClients) {
 r.makeActiveIfNeeded(mStarting);
 }
} else {
 r.makeVisibleIfNeeded(mStarting, mNotifyClients);
}

这些就是去执行启动Activity相关的逻辑,这里也先略过。

以下是AMS发起创建新进程的时序图:

sequenceDiagram
AMS->>+ProcessList: startProcessLocked
ProcessList->>ProcessList: newProcessRecordLocked
ProcessList->>ProcessList: startProcess
ProcessList->>+ZygoteProcess: startViaZygote
ZygoteProcess->>ZygoteProcess: openZygoteSocketIfNeeded
ZygoteProcess->>ZygoteProcess: zygoteSendArgsAndGetResult
ZygoteProcess->>ZygoteServer: send args and get Result
ZygoteProcess-->>-ProcessList: return result with pid
ProcessList-->>-AMS: return ProcessRecord

以下是Zygote侧处理fork进程请求的时序图:

sequenceDiagram
ZygoteInit->>ZygoteServer: runSelectLoop
loop 无限循环
ZygoteServer->>ZygoteConnection: processCommand
ZygoteConnection->>Zygote: forkAndSpecialize
ZygoteConnection-->>ZygoteConnection: (Parent): notify child pid by socket
ZygoteServer-->>ZygoteInit: return command(child process)
end
rect rgb(191, 223, 255)
note right of ZygoteInit: fork完子进程执行的内容
ZygoteConnection->>ZygoteInit: zygoteInit
ZygoteInit->>ZygoteInit: nativeZygoteInit
ZygoteInit->>AppRuntime: onZygoteInit
ZygoteInit->>RuntimeInit: findStaticMain
RuntimeInit-->>ZygoteInit: return ActivityThread Main function caller
ZygoteInit->>ActivityThread: main
ActivityThread->>AMS: attachApplication
AMS->>ActivityThread: bindApplication
end

以上就是应用进程启动的完整流程,为了使得流程更加简洁,其中不太重要的步骤有作省略。如果你也对于Android系统源码感兴趣,欢迎与我交流。博文因为个人局限,也难免会出现差错,欢迎大家指正。

看完评论一下吧

Android Binder源码分析:AIDL及匿名服务传输

2024年9月7日 17:57

前面介绍的通过ServiceManager添加服务和获取服务,这些服务都是有名称的,我们可以通过ServiceManager来获取它。除此之外Android系统中还有一类Binder服务是匿名它,它们如何让客户端获得代理对象,并且使用呢,本文就一探究竟。

AIDL 介绍

AIDL全称为Android接口定义语言,是Android系统提供的一款可供用户用来抽象IPC的工具,它提供了语法让我们来定义跨进程通讯的服务接口,也就是.aidl文件,它也提供了工具,帮助我们把定义文件专程目标语言的代码。

我们自己使用AIDL创建的服务,或者一部分系统内的服务,比如IWindowSessionIApplicationThread等,这些多是运行在App进程,一般都不是系统服务,因此都是匿名的。我们这里以IApplicationThread来分析AIDL创建的匿名服务是怎么传递Binder给使用端的。

IApplicationThread来分析,则服务端是我们的App进程,而客户端则是system_server进程。作为AIDL创建的Binder,首先会有一个AIDL文件,这里是IApplicationThread.aidl,其中定义了一些跨进程调用的方法,部分内容如下:

package android.app;
...
oneway interface IApplicationThread {
void scheduleReceiver(in Intent intent, in ActivityInfo info,
in CompatibilityInfo compatInfo,
int resultCode, in String data, in Bundle extras, boolean sync,
int sendingUser, int processState);
@UnsupportedAppUsage
void scheduleStopService(IBinder token);
...
}

当前Android AIDL已经支持生成Java、C++、Rust的代码,对于IApplicationThread这里我们只需关注生成Java版本的代码即可。生成的代码在IApplicationThread当中,大概如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
public interface IApplicationThread extends android.os.IInterface {
public static class Default implements android.app.IApplicationThread {
 @Override
 public void scheduleReceiver(android.content.Intent
 ...) throws android.os.RemoteException {}

 @Override
 public android.os.IBinder asBinder() {
 return null;
 }
}

public abstract static class Stub extends android.os.Binder
 implements android.app.IApplicationThread {
 public Stub() {
 this.attachInterface(this, DESCRIPTOR);
 }

 public static android.app.IApplicationThread asInterface(android.os.IBinder obj) {
 if ((obj == null)) {
 return null;
 }
 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 if (((iin != null) && (iin instanceof android.app.IApplicationThread))) {
 return ((android.app.IApplicationThread) iin);
 }
 return new android.app.IApplicationThread.Stub.Proxy(obj);
 }

 @Override
 public android.os.IBinder asBinder() {
 return this;
 }

 public static java.lang.String getDefaultTransactionName(int transactionCode) {
 switch (transactionCode) {
 case TRANSACTION_scheduleReceiver:{
 return "scheduleReceiver";
 }
 case TRANSACTION_scheduleCreateService: {
 return "scheduleCreateService";
 }
 default: {
 return null;
 }
 }
 }

 public java.lang.String getTransactionName(int transactionCode) {
 return this.getDefaultTransactionName(transactionCode);
 }

 @Override
 public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
 throws android.os.RemoteException {
 java.lang.String descriptor = DESCRIPTOR;
 if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION
 && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
 data.enforceInterface(descriptor);
 }
 switch (code) {
 case INTERFACE_TRANSACTION:{
 reply.writeString(descriptor);
 return true;
 }
 }

 switch (code) {
 case TRANSACTION_scheduleReceiver:{
 android.content.Intent _arg0;
 _arg0 = data.readTypedObject(android.content.Intent.CREATOR);
 ...
 int _arg8;
 _arg8 = data.readInt();
 data.enforceNoDataAvail();
 this.scheduleReceiver(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8);
 break;
 }
 ...
 default:{
 return super.onTransact(code, data, reply, flags);
 }
 }
 return true;
 }

 private static class Proxy implements android.app.IApplicationThread {
 private android.os.IBinder mRemote;

 Proxy(android.os.IBinder remote) {
 mRemote = remote;
 }

 @Override
 public android.os.IBinder asBinder() {
 return mRemote;
 }

 public java.lang.String getInterfaceDescriptor() {
 return DESCRIPTOR;
 }

 @Override
 public void scheduleReceiver(
 android.content.Intent intent,
 ...
 int processState) throws android.os.RemoteException {
 android.os.Parcel _data = android.os.Parcel.obtain();
 try {
 _data.writeInterfaceToken(DESCRIPTOR);
 ...
 _data.writeTypedObject(intent, 0);
 boolean _status = mRemote.transact(
 Stub.TRANSACTION_scheduleReceiver, _data, null, android.os.IBinder.FLAG_ONEWAY);
 } finally {
 _data.recycle();
 }
 }
 ...
 }

 public static final java.lang.String DESCRIPTOR = "android.app.IApplicationThread";
 static final int TRANSACTION_scheduleReceiver = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
 static final int TRANSACTION_scheduleCreateService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
...
 public int getMaxTransactionId() {
 return 57;
 }
}
...
public void scheduleReceiver(
android.content.Intent intent,
...
int processState)
throws android.os.RemoteException;

}

对于Java代码,AIDL会生成一个跟AIDL同名的接口,同时继承自IInterface,同时还会创建内部类,分别为Default和Stub。Default为默认实现,大多数情况下是没有的,因为我们自己会实现。而Stub为抽象类,我们自己实现的时候会使用它,它以及继承自Binder,AIDL工具帮助我们把Parcel读写相关的代码已经生成,我们只需要去继承它,实现业务逻辑即可。而Stub中还有一个内部类Proxy,这个类用于Binder服务的客户端使用。对于IApplicationThread的实现,在ActivityThread当中。

匿名服务的传输

那么ActivityServiceManager(之后简称AMS)是怎么拿到ApplicationThread的呢,ActivityThread的attach方法则是这一切的入口:

1
2
3
4
5
6
final IActivityManager mgr = ActivityManager.getService();
try {
 mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
 throw ex.rethrowFromSystemServer();
}

代码中,首先拿到AMS的binder客户端类,这里也就是IActivityManager$Stub$Proxy,因为它也用了AIDL,所以跟我们ApplicationThread的类是类似的,具体如何拿到的,这个之前ServiceManager分析getService的时候已经分析过了,这里不看了。我们可以看一下它的attachApplication方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Override public void attachApplication(android.app.IApplicationThread app, long startSeq) throws android.os.RemoteException
{
 android.os.Parcel _data = android.os.Parcel.obtain();
 android.os.Parcel _reply = android.os.Parcel.obtain();
 try {
 _data.writeInterfaceToken(DESCRIPTOR);
 _data.writeStrongInterface(app);
 _data.writeLong(startSeq);
 boolean _status = mRemote.transact(Stub.TRANSACTION_attachApplication, _data, _reply, 0);
 _reply.readException();
 }
 finally {
 _reply.recycle();
 _data.recycle();
 }
}

这里也是跟我们之前addService类似,把binder写入到Parcel中去,因为App进程这里相当于是ApplicationThread它的服务端,因此这里写入的type为BINDER_TYPE_BINDER,而调用mRemote.transact则为调用BinderProxy的同名方法,我们知道最终会调用到IPCThreadStatetransact方法,从而调用binder驱动。

类似于getService的方法,AMS所在的system_server进程会收到BR_TRANSACTION命令,在其中解析数据知道调用的是TRANSACTION_attachApplication这个业务命令,进而使用readStrongInterface来获取到binder的代理对象BinderProxy。具体代码在IActivityManager.StubonTransact中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
case TRANSACTION_attachApplication:
{
 android.app.IApplicationThread _arg0;
 _arg0 = android.app.IApplicationThread.Stub.asInterface(data.readStrongBinder());
 long _arg1;
 _arg1 = data.readLong();
 data.enforceNoDataAvail();
 this.attachApplication(_arg0, _arg1);
 reply.writeNoException();
 break;
}

到这样调用AMS服务端的attachApplication的时候就能使用IApplicationThread所提供的方法了。

Binder驱动中binder节点的处理

但是看到这里,有个问题,就是我们是匿名的binder,驱动怎么是处理的呢。这个需要去看一下binder驱动的源码,不过就不具体看调用流程了,直接看生成创建node和handle部分的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static int binder_translate_binder(struct flat_binder_object *fp,
 struct binder_transaction *t,
 struct binder_thread *thread)
{
 struct binder_node *node;
 struct binder_proc *proc = thread->proc;
 struct binder_proc *target_proc = t->to_proc;
 struct binder_ref_data rdata;
 int ret = 0;

 node = binder_get_node(proc, fp->binder);
 if (!node) {
 node = binder_new_node(proc, fp);
 if (!node)
 return -ENOMEM;
 }
 ...
 ret = binder_inc_ref_for_node(target_proc, node,
 fp->hdr.type == BINDER_TYPE_BINDER,
 &thread->todo, &rdata);
 ...
 if (fp->hdr.type == BINDER_TYPE_BINDER)
 fp->hdr.type = BINDER_TYPE_HANDLE;
 else
 fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
 fp->binder = 0;
 fp->handle = rdata.desc;
 fp->cookie = 0;

 ...
 done:
 binder_put_node(node);
 return ret;
}

可以看到对于通过Parcel调用经过binder驱动的binder对象,binder驱动都会给他们创建一个binder_node,并且为其设置handle,在传输到客户端的时候还会把type设置为BINDER_TYPE_HANDLE。 这样我们就对整给流程有所了解了,如果读者还想窥探更多的细节,则需要自行去阅读binder驱动的源码了。

开发者如何使用匿名服务

这上面介绍的部分是我们使用系统的服务来获取AIDL创建的服务,对于应用开发者来说有什么办法呢。我们可以通过AIDL+Service来实现,Android的四大组件之一的Service,它提供了通过bindService的方式来启动服务。在它的onBind中就可以返回IBinder,Android Framework会帮助我们调用操作代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void handleBindService(BindServiceData data) {
 CreateServiceData createData = mServicesData.get(data.token);
 Service s = mServices.get(data.token);
 if (s != null) {
 try {
 ....
 try {
 if (!data.rebind) {
 IBinder binder = s.onBind(data.intent);
 ActivityManager.getService().publishService(
 data.token, data.intent, binder);
 } else {
 ...
 }
 } catch (RemoteException ex) {
 throw ex.rethrowFromSystemServer();
 }
 } catch (Exception e) {
 ....
 }
 }
}

可以看到在ActivityThreadhandleBindService方法 中,我们在拿到Service所提供的IBinder之后,AMS会调用publishService,我们可以在ServiceConnection回调中拿到Binder的代理对象,之后就可以进行跨进程通讯了。

另外Android Framework还为我们提供了Messenger,其实现为Service+AIDL+Handler,让我们不用自己写AIDL,我们自己定义Service的时候使用Messenger和Handler就可以实现跨进程通信了。

总结

到此为止,我们已经分析了Binder服务管家ServiceManager的启动、使用ServiceManger添加服务和查找服务以及匿名服务的传递,在此过程中我们了解了进程是如何与Binder驱动交互的,以及binder调用过程中的会执行的方法等,我们对于Binder就有了一个全面的了解。在本文还简单介绍 了应用开发者如何使用Binder,有了这些基础,我们后面分析Android系统其他部分的代码就会更加容易了。当然关于Binder驱动的代码,BInder线程池的管理这两块还没有分析,读者感性确可以自行阅读,也可查看其他博主的文章。

如果你也对于Android系统源码感兴趣,欢迎与我交流。博文因为个人局限,也难免会出现差错,欢迎大家指正。

看完评论一下吧

Android Binder源码分析:添加服务和获取服务解析

2024年9月6日 19:22

通过ServiceManager添加服务和获取服务分别为addServicegetService,两者流程上其实是有一些类似的,其中我们可以看到binder通讯的全过程。为了让内容更有意义,添加服务选择从Java层的代码触发,获取服务则选择从Native层触发。

添加服务

我们以添加ActivityManagerService为例分析添加一个Service。首先先画个简单的流程图,介绍涉及到的类和调用的方法,不过步骤进行了省略,详细看后面的代码解析。

ServiceManagerProxy请求添加Service

其代码在ActivityManagerServicesetSystemProcess方法当中,具体调用如下:

1
2
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);

具体实现如下:

1
2
3
4
5
6
7
8
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static void addService(String name, IBinder service, boolean allowIsolated,
 int dumpPriority) {
 try {
 getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
 } catch (RemoteException e) {
 }
}

其中getIServiceManager的代码我们已经分析过了,我们之前拿到的IServiceManager的实例为ServiceManagerProxy,这里可以直接去看它的这个方法:

1
2
3
4
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
 throws RemoteException {
 mServiceManager.addService(name, service, allowIsolated, dumpPriority);
}

这里调用了mServiceManager的同名方法,而这个成员变量的初始化如下:

1
mServiceManager = IServiceManager.Stub.asInterface(remote);

这里的remote就是我们之前构造函数传入的BinderProxy,而上面的函数后我们发获取到的对象则为IServiceMaanager.Stub.Proxy,我们可以看一下它的同名方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Override public void addService(java.lang.String name, android.os.IBinder service, boolean allowIsolated, int dumpPriority) throws android.os.RemoteException
{
 android.os.Parcel _data = android.os.Parcel.obtain();
 android.os.Parcel _reply = android.os.Parcel.obtain();
 try {
 _data.writeInterfaceToken(DESCRIPTOR);
 _data.writeString(name);
 _data.writeStrongBinder(service);
 _data.writeBoolean(allowIsolated);
 _data.writeInt(dumpPriority);
 boolean _status = mRemote.transact(Stub.TRANSACTION_addService, _data, _reply, 0);
 _reply.readException();
 }
 finally {
 _reply.recycle();
 _data.recycle();
 }
}

这里我们看到就是把设置的数据和binder写入到Parcel之后 调用transact。这里我们可以看一下Parcel首先是写入了InterfaceToken,也就是IServiceManager的描述符,其次才是其他内容。我们主要关注一下如何写入Binder的。其最终调用的方法在android_os_Parcel.cpp中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
 if (parcel != NULL) {
 const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
 if (err != NO_ERROR) {
 signalExceptionForError(env, clazz, err);
 }
 }
}

我们主要关注第5行,这里有一个ibinderForJavaObject,用于从javaobject中拿到binder的native对象,我们可以看一下其源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
 if (obj == NULL) return NULL;

 // 如果是Binder实例
 if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
 JavaBBinderHolder* jbh = (JavaBBinderHolder*)
 env->GetLongField(obj, gBinderOffsets.mObject);
 return jbh->get(env, obj);
 }
 // 如果是BinderProxy实例
 if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
 return getBPNativeData(env, obj)->mObject;
 }
 return NULL;
}

BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
 return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}

这个地方会判断我们的传过来的javaobject是Binder的实例还是BinderProxy的实例,前者对应Binder的服务端,后者对应的是客户端,我们刚刚传过来的AMS则是服务端。这里是从javaobject拿到mObject成员变量,对应native的类JavaBBinderHolder,最后调用它的get方法拿到JavaBinder对象。此处算是完成了我们Java层的Binder在Native层的对应对象的获取。现在就可以看看ParcelwriteStrongBinder方法了:

1
2
3
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) {
 return flattenBinder(val);
}

其中又调用了flattenBinder,这个方法比较长,我们先一点点的贴代码:

1
2
3
4
5
6
7
BBinder* local = nullptr;
if (binder) local = binder->localBinder();
if (local) local->setParceled();

if (isForRpc()) {
...
}

这里binder是我们刚刚的拿到的JavaBBinder,它的localBinder()实现如下:

1
2
3
BBinder* BBinder::localBinder() {
 return this;
}

也就是说返回了自己。另外这里我们不是RPC,所以其中的代码我们不需要关注,继续看后面的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
flat_binder_object obj;
if (binder != nullptr) {
 if (!local) {
 ...//如果我们传入的不是BBinder,而是BpBinder执行这里的逻辑,省略
 } else {
 int policy = local->getMinSchedulerPolicy();
 int priority = local->getMinSchedulerPriority();

 if (policy != 0 || priority != 0) {
 // override value, since it is set explicitly
 schedBits = schedPolicyMask(policy, priority);
 }
 obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
 ....
 obj.hdr.type = BINDER_TYPE_BINDER;
 obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
 obj.cookie = reinterpret_cast<uintptr_t>(local);
 }
} else {
 ...
}
obj.flags |= schedBits;

上面的代码主要是将binder的一些参数拍平放到flat_binder_object当中。其中binder是放置到cookie字段,binder的弱引用放到了binder字段。

1
2
3
4
status_t status = writeObject(obj, false);
if (status != OK) return status;

return finishFlattenBinder(binder);

这里才开始真正的把数据写入 ,可以先看看这个writeObject方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
 const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
 const bool enoughObjects = mObjectsSize < mObjectsCapacity;
 if (enoughData && enoughObjects) {
restart_write:
 *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; //把数据写如内存
 ...
 // Need to write meta-data?
 if (nullMetaData || val.binder != 0) {
 mObjects[mObjectsSize] = mDataPos;
 acquire_object(ProcessState::self(), val, this);
 mObjectsSize++;
 }
 return finishWrite(sizeof(flat_binder_object)); //调整dataPos和当前DateSize
 }
 ...
 goto restart_write;
}

上面主要就是把binder写入内存当中,其他的则是处理内存不足的情况,有申请内存的代码,这里我们无须关注。可以在看一下前面的finishFlattenBinder方法:

1
2
3
4
5
status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder) {
 internal::Stability::tryMarkCompilationUnit(binder.get());
 int16_t rep = internal::Stability::getRepr(binder.get());
 return writeInt32(rep);
}

这个方法主要为binder设置Repr,并且把值也写入到Parcel当中去,默认值为Level::SYSTEM,我们不再深入看其代码。

到这里大概就看完了Parcel写数据的代码了。可以看看transact方法,这里的mRemoteBinderProxy,它的transact方法中主要调用了一下代码:

1
2
3
4
5
final boolean result = transactNative(code, data, reply, flags);
if (reply != null && !warnOnBlocking) {
 reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
}
return result;

它的native实现在android_util_Binder.cpp中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
 jint code, jobject dataObj, jobject replyObj, jint flags)
{
 Parcel* data = parcelForJavaObject(env, dataObj); //从java层的Parcel对象获取native层的Parcel对象
 ...
 Parcel* reply = parcelForJavaObject(env, replyObj);
 ...
 IBinder* target = getBPNativeData(env, obj)->mObject.get(); //获取native层的BinderProxy对象
 if (target == NULL) {
 ...
 return JNI_FALSE;
 }

 status_t err = target->transact(code, *data, reply, flags);
 ...
 return JNI_FALSE;
}

上面主要就 是获取native层的Parcel对象和Binder对象,并且调用binder的transact方法。这里的Binder对象是什么呢,回顾之前分析的javaObjectForIBinder方法,可知此处拿到的应该是BpBinder对象。我们就可以看它的代码了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
status_t BpBinder::transact(
 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
 // 只有binder或者的时候才能执行
 if (mAlive) {
 bool privateVendor = flags & FLAG_PRIVATE_VENDOR;
 // 用户层的flag移除
 flags = flags & ~FLAG_PRIVATE_VENDOR;

 if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {
 ... //Stability 相等判断,此处略过
 }

 status_t status;
 if (CC_UNLIKELY(isRpcBinder())) {
 ...
 } else {
 status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
 }
 ....
 if (status == DEAD_OBJECT) mAlive = 0;

 return status;
 }

 return DEAD_OBJECT;
}

因为ServiceManager的id为0,此处binderHandle()拿到的值应为0。此处主要也是调用了IPCThreadStatetransact方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
status_t IPCThreadState::transact(int32_t handle,
 uint32_t code, const Parcel& data,
 Parcel* reply, uint32_t flags)
{
 status_t err;

 flags |= TF_ACCEPT_FDS;
 err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

 if (err != NO_ERROR) {
 if (reply) reply->setError(err);
 return (mLastError = err);
 }

 if ((flags & TF_ONE_WAY) == 0) {
 if (reply) {
 err = waitForResponse(reply);
 } else {
 Parcel fakeReply;
 err = waitForResponse(&fakeReply);
 }
 } else {
 err = waitForResponse(nullptr, nullptr);
 }

 return err;
}

这里主要调用了两个方法,分别是writeTransactionDatawaitForResponse,我们分别看一下。首先是writeTransactionData,它的第一个参数为BC_TRANSACTION,这是用于与Binder驱动交互的命令,除了这个之外还有其他一些,可以在binder.h当中找到。现在可以看writeTransactionData的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
 binder_transaction_data tr;

 tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
 tr.target.handle = handle;
 tr.code = code;
 tr.flags = binderFlags;
 tr.cookie = 0;
 tr.sender_pid = 0;
 tr.sender_euid = 0;

 const status_t err = data.errorCheck();
 if (err == NO_ERROR) {
 tr.data_size = data.ipcDataSize();
 tr.data.ptr.buffer = data.ipcData();
 tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
 tr.data.ptr.offsets = data.ipcObjects();
 } else if (statusBuffer) {
 ...
 } else {
 return (mLastError = err);
 }

 mOut.writeInt32(cmd);
 mOut.write(&tr, sizeof(tr));

 return NO_ERROR;
}

这个方法中所做的事情为,把我们传入的data和code以及要调用的binder的id等都放到binder_transaction_data中去,同时又把这个tr和调用binder驱动的命令BC_TRANSACTION一起写入到mOut当中去,这个mOut也是一个Parcel对象。到这里,数据都写完了,但是binder驱动在那里读取处理这个数据呢,我们继续看waitForResponse,前面我们因为有传reply过来,因此会调用到第17行的waitForResponse方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
 uint32_t cmd;
 int32_t err;

 while (1) {
 if ((err=talkWithDriver()) < NO_ERROR) break;
 err = mIn.errorCheck();
 if (err < NO_ERROR) break;
 if (mIn.dataAvail() == 0) continue;

 cmd = (uint32_t)mIn.readInt32();

 switch (cmd) {
 case BR_ONEWAY_SPAM_SUSPECT:
 [[fallthrough]];
 ......
 case BR_ACQUIRE_RESULT:
 {
 const int32_t result = mIn.readInt32();
 if (!acquireResult) continue;
 *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
 }
 goto finish;

 case BR_REPLY:
 {
 binder_transaction_data tr;
 err = mIn.read(&tr, sizeof(tr));
 if (err != NO_ERROR) goto finish;

 if (reply) {
 if ((tr.flags & TF_STATUS_CODE) == 0) {
 reply->ipcSetDataReference(
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
 tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
 tr.offsets_size/sizeof(binder_size_t),
 freeBuffer);
 } else {
 ....
 } else {
 ...
 continue;
 }
 }
 goto finish;

 default:
 err = executeCommand(cmd);
 if (err != NO_ERROR) goto finish;
 break;
 }
 }

finish:
 if (err != NO_ERROR) {
 if (acquireResult) *acquireResult = err;
 if (reply) reply->setError(err);
 mLastError = err;
 }

 return err;
}

talkWithDriver分析

这里开启了一个while的无限循环,首先调用talkWithDriver,看名字就知道是与Binder驱动进行交互,这里首先会看看这个方法有没有报错,没有报错又会检查mIn是否有报错。我们前面看到过mOut,这里又有mIn,它们是用来做什么的呢,我们看一下talkWithDriver,可以发现一些东西:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
 if (mProcess->mDriverFD < 0) {
 return -EBADF;
 }

 binder_write_read bwr;

 const bool needRead = mIn.dataPosition() >= mIn.dataSize();
 const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

 bwr.write_size = outAvail;
 bwr.write_buffer = (uintptr_t)mOut.data();

 // This is what we'll read.
 if (doReceive && needRead) {
 bwr.read_size = mIn.dataCapacity();
 bwr.read_buffer = (uintptr_t)mIn.data();
 } else {
 bwr.read_size = 0;
 bwr.read_buffer = 0;
 }

 // 无数据需要读写,直接返回
 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

 bwr.write_consumed = 0;
 bwr.read_consumed = 0;
 status_t err;
 do {

#if defined(__ANDROID__)
 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
 err = NO_ERROR;
 else
 err = -errno;
#else
 err = INVALID_OPERATION;
#endif
 if (mProcess->mDriverFD < 0) {
 err = -EBADF;
 }
 } while (err == -EINTR);


 if (err >= NO_ERROR) {
 if (bwr.write_consumed > 0) {
 if (bwr.write_consumed < mOut.dataSize())
 ...
 else {
 mOut.setDataSize(0);
 processPostWriteDerefs();
 }
 }
 if (bwr.read_consumed > 0) {
 mIn.setDataSize(bwr.read_consumed);
 mIn.setDataPosition(0);
 }
 ...
 return NO_ERROR;
 }

 return err;
}

这个方法传参的默认值为true,也就是需要接受结果。在这里我们看到有一个新的数据结构binder_write_read,此处会把mOut中的数据指针写入到它的write_buffer当中,同时把mIn的数据指针写入到read_buffer中,此处的写指的是向binder驱动中写。随后我们看到是在一个循环当中调用系统调用ioctl来与binder驱动进行交互,这里使用循环的原因是,当我们调用这个系统调用的时候可能会遇到遇到中断,我们之前的调用未能执行,因此需要一直等待到执行为止。

到这里我们就分析完了添加Service调用端的所有代码,此时我们需要看一下ServiceManager服务端与Binder进行交互的代码。

ServiceManager服务端处理添加Service

我们之前分析ServiceManager启动的时候,知道最后会注册Looper的监听,当Binder驱动有消息的时候,BinderCallbak的handleEvent就会执行去处理,那么当我们在客户端请求添加Binder服务的时候,这里也会执行。这个方法中执行了如下代码:

1
IPCThreadState::self()->handlePolledCommands();

这里我们可以看一下详细的源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
status_t IPCThreadState::handlePolledCommands()
{
 status_t result;
 do {
 //读取缓存数据知道处理完成
 result = getAndExecuteCommand();
 } while (mIn.dataPosition() < mIn.dataSize());
 //减少binder的引用数量,此处也会和驱动交互
 processPendingDerefs();
 //若有为执行的命令,全部执行
 flushCommands();
 return result;
}

此处我们主要关注getAndExecuteCommand方法,后面都已经加了注释,此处不需要详细关注。getAndExecuteCommand方法当中也是首先调用talkWithDriver方法,这个方法前面分析过了,不再分析,这样执行完之后,mIn当中就会拿到客户端请求传输过来的数据了,之后就从数据中拿取命令和数据进行执行,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32(); //读取命令
pthread_mutex_lock(&mProcess->mThreadCountLock); //为了增加线程计数上锁
mProcess->mExecutingThreadsCount++;
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
 mProcess->mStarvationStartTimeMs == 0) {
 mProcess->mStarvationStartTimeMs = uptimeMillis();
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);

result = executeCommand(cmd); //执行命令

pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
if (mProcess->mWaitingForThreads > 0) {
 pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);

代码很多,但是大多都是为了给binder线程计数增减的。我们主要去看一下executeCommand中的代码,该方法中代码很多,而我们在客户端执行的是BC_TRANSACTION,因此这里应该收到的是BR_TRANSACTION命令,因此只需要看该分支的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;

switch ((uint32_t)cmd) {
 ...
 case BR_TRANSACTION:
 binder_transaction_data_secctx tr_secctx;
 binder_transaction_data& tr = tr_secctx.transaction_data;
 result = mIn.read(&tr, sizeof(tr)); //读取binder携带过来的数据到tr中

 Parcel buffer;
 //将数据的引用放入Parcel当中
 buffer.ipcSetDataReference(
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
 tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
 tr.offsets_size/sizeof(binder_size_t), freeBuffer);
 //设置调用这的uid,pid,flag等信息
 mCallingPid = tr.sender_pid;
 mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
 mCallingUid = tr.sender_euid;
 mLastTransactionBinderFlags = tr.flags;
 if (tr.target.ptr) { //ServiceManager的binder无ptr
 //非serviceManager的binder,tr.cookie为本地的BBinder对象指针
 if (reinterpret_cast<RefBase::weakref_type*>(
 tr.target.ptr)->attemptIncStrong(this)) {
 error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
 &reply, tr.flags);
 reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
 } else {
 error = UNKNOWN_TRANSACTION;
 }

 } else {
 //ServiceManager使用the_context_object这个BBinder对象。
 error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
 }
 if ((tr.flags & TF_ONE_WAY) == 0) {
 if (error < NO_ERROR) reply.setError(error);

 constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
 sendReply(reply, (tr.flags & kForwardReplyFlags)); //写入回复
 } else {
 ...
 }
 ...
}

return result;

上面的代码已经做了省略,逻辑就是首先从mIn这块内存中拿到数据,并且放Parcel中,随后把uid,pid相关的属性设置到当前进程。之后是获取BBinder对象去执行transact方法,对于普通的binder,对于普通的binder,会ptr这个字段,并且tr.cookie就是本地的BBinder对象指针,而对于ServiceManager,这里就会使用在启动ServiceManager时候调用setTheContextObject所设置的BBinder对象,也就是服务端的ServiceManager。这里transact执行完成之后会调用sendReply将执行结果通过binder驱动传递回binder调用端,从而完成整个流程。这里先看transact,分析完再来分析sendReply

transact方法在BBinder类当中,在其中会调用onTransact方法,而到ServiceManager,它的onTransact的实现在BnServiceManager当中,这个类则是通过AIDL工具生成的。因为没有源码,根据经验我们这边可以知道它会调用ServiceManageraddService方法,而其中最重要的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
mNameToService[name] = Service {
 .binder = binder,
 .allowIsolated = allowIsolated,
 .dumpPriority = dumpPriority,
 .debugPid = ctx.debugPid,
};

auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
 for (const sp<IServiceCallback>& cb : it->second) {
 mNameToService[name].guaranteeClient = true;
 // permission checked in registerForNotifications
 cb->onRegistration(name, binder);
 }
}

看代码可知道,这里把Binder放到Service结构体当中,随后放入mNameToService当中,mNameToService是一个map。而mNameToRegistrationCallback中为服务注册的回调,当注册完成之后会调用它的onRegistration方法。

前面我们还有一个sendReply方法我们还未分析,这里再看一下:

1
2
3
4
5
6
7
8
9
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
 status_t err;
 status_t statusBuffer;
 err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
 if (err < NO_ERROR) return err;

 return waitForResponse(nullptr, nullptr);
}

writeTransactionData当中就是把我们的reply打包成为binder_transaction_data写入mOut当中,这里的命令为BC_REPLY,执行完之后调用waitForResponse,其中会调用talkWithDriver来回应,之后便结束了服务端的相应。客户端随后可以读取客户端的mIn数据可以获取reply的数据。到这里就分析完了Service注册的流程。

获取服务(getService)分析

之前分析getIServiceManageraddService我们都是从java层的代码出发去往后走分析代码,而getService其实有一些地方跟他们是类似的,为了减少重复流程的分析,这里从Native层的使用场景出发。这里以获取ICameraService为例。

获取defaultServiceManager

我们的起点在frameworks/av/camera/ndk/impl/ACameraManager.cpp当中,调用代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const char* kCameraServiceName = "media.camera";
....

sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
 binder = sm->getService(String16(kCameraServiceName));
 if (binder != nullptr) {
 break;
 }
 usleep(kCameraServicePollDelay);
} while(true);

这里使用defaultServiceManager来拿到ServiceManager,其源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//frameworks/native/libs/binder/IServiceManager.cpp
sp<IServiceManager> defaultServiceManager()
{
 std::call_once(gSmOnce, []() {
 sp<AidlServiceManager> sm = nullptr;
 while (sm == nullptr) {
 sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
 if (sm == nullptr) {
 sleep(1);
 }
 }

 gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);
 });

 return gDefaultServiceManager;
}

这个代码跟我们之前java层的代码比较类似,也是先拿ContentObject,而ServiceManagerShim相当于是native层的ServiceManager的代理。native层的代码因为不需要把对象转成Java的消耗,代码其实更加简单一点。这里我们拿到了ServiceManagerShim,就可以继续去看它的getService方法了。

请求getService

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sp<IBinder> ServiceManagerShim::getService(const String16& name) const
{
 static bool gSystemBootCompleted = false;

 sp<IBinder> svc = checkService(name);
 if (svc != nullptr) return svc;

 const bool isVendorService =
 strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0;
 constexpr int64_t timeout = 5000;
 int64_t startTime = uptimeMillis();
 // 如果是Vendor的服务,不能够访问系统的属性
 if (!gSystemBootCompleted && !isVendorService) {
#ifdef __ANDROID__
 char bootCompleted[PROPERTY_VALUE_MAX];
 property_get("sys.boot_completed", bootCompleted, "0");
 gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false;
#else
 gSystemBootCompleted = true;
#endif
 }
 // 如果拿不到binder service就等待,系统服务和vendor时间有区分,直到超时才停止
 const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
 int n = 0;
 while (uptimeMillis() - startTime < timeout) {
 n++;
 usleep(1000*sleepTime);

 sp<IBinder> svc = checkService(name);
 if (svc != nullptr) {
 return svc;
 }
 }
 return nullptr;
}

这里就是调用checkService去获取Service,源码如下:

1
2
3
4
5
6
7
8
sp<IBinder> ServiceManagerShim::checkService(const String16& name) const
{
 sp<IBinder> ret;
 if (!mTheRealServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
 return nullptr;
 }
 return ret;
}

这里我们调用了mTheRealServiceManagercheckService方法,这个变量的实例为ServiceManager的BpBinder子类,也是由AIDL生成,其代码如下:

1
2
3
4
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name);
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();

这里跟之前分析addService的部分类似,只有最后多了一个readStrongBinder,addServicewriteStrongBinder到data,这里是读取binder调用返回的数据。后续流程也跟addService类似,这里就不再分析了。我们更关注这个binder我们是怎么拿到的,因此需要看三个地方的代码,一个是ServiceManger拿到binder并且写入到驱动给我们的过程,第二个地方是IPCThreadState当中接收数据的处理,最后就是通过readStrongBinder拿到binder的处理了。

客户端请求获取Binder服务的流程大概如下图所示:

sequenceDiagram
ServiceManagerShim->>ServiceManagerShim: defaultServiceManager()
ServiceManagerShim->>ServiceManagerShim:getService()
ServiceManagerShim->>ServiceManagerShim:checkService()
ServiceManagerShim->>+BpServiceManager:checkService()
BpServiceManager->>+BpBinder: transact()
BpBinder->>+IPCThreadState: transact()
IPCThreadState->>IPCThreadState: writeTransactionData()
IPCThreadState->>IPCThreadState: waitForResponse()
IPCThreadState->>IPCThreadState: talkWithDriver()
IPCThreadState->>+BinderDriver: ioctl(BC_TRANSACTION)
BinderDriver-->>-IPCThreadState: reply:BR_REPLY
IPCThreadState-->>-BpBinder: return resut
BpBinder-->>-BpServiceManager: return result
BpServiceManager->>BpServiceManager: readStrongBinder()
BpServiceManager-->>-ServiceManagerShim: return binder

ServiceManager服务端getService

前面分析addService我们已经知道服务端调用路径是BBinder.transcat–>BnServiceManager.onTransact–>ServiceManger.addService,这里的服务端也是类似,具体可以看下面的流程图。

sequenceDiagram
BinderDriver-->IPCThreadState:handlePolledCommands
loop mIn.dataPosition < mIn.dataSize (当输入数据未处理完)
IPCThreadState->>+IPCThreadState: getAndExecuteCommand
IPCThreadState->>IPCThreadState: executeCommand: BR_TRANSACTION
IPCThreadState->>BBinder: transact()
BBinder->>+BnServiceManager: onTransact()
BnServiceManager->>+ServiceManager: getService()
ServiceManager-->>-BnServiceManager: return Binder
BnServiceManager->>BnServiceManager: writeStrongBinder()
BnServiceManager-->-BBinder: return reply Parcel
BBinder-->>IPCThreadState: return reply
IPCThreadState->>+IPCThreadState: sendReply
IPCThreadState->>IPCThreadState:writeTransactionData
IPCThreadState->>+IPCThreadState:waitForResponse(null, null)
IPCThreadState->>IPCThreadState: talkWithDriver
IPCThreadState->>BinderDriver: ioctl:BC_REPLY
IPCThreadState-->>-IPCThreadState::
IPCThreadState-->-IPCThreadState: finishSendReply
end

我们就省略与Binder交互的许多代码,可以直接去看getService的代码了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
 *outBinder = tryGetService(name, true);
 return Status::ok();
}

sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
 auto ctx = mAccess->getCallingContext();

 sp<IBinder> out;
 Service* service = nullptr;
 if (auto it = mNameToService.find(name); it != mNameToService.end()) {
 service = &(it->second);

 if (!service->allowIsolated) { //是否允许多用户环境运行
 uid_t appid = multiuser_get_app_id(ctx.uid);
 bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;

 if (isIsolated) {
 return nullptr;
 }
 }
 out = service->binder;
 }

 if (!mAccess->canFind(ctx, name)) { //SELinux 权限检查
 return nullptr;
 }

 if (!out && startIfNotFound) {
 tryStartService(name);
 }

 return out;
}

ServiceManger中获取Binder就是从我们之前添加Service的时候的那个ServiceMap中查找,当查找后做一些权限检查,当找不到的情况下,因为我们传如的startIfNotFound,因此会调用tryStartService去启动对应的Service,其代码如下:

1
2
3
4
5
6
void ServiceManager::tryStartService(const std::string& name) {
 std::thread([=] {
 if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
 ...
 }).detach();
}

代码很简单,就是启动了一个线程,其中设置系统的properties,系统便会尝试启动这个服务,具体我们这里就不分析了。

IPCThreadState接收数据处理

在服务端发送数据时候会调用binder执行BC_REPLY,而客户端后收到BR_REPLY命令,也就是会执行waitForResponse中的如下部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
....
case BR_REPLY:
 {
 binder_transaction_data tr;
 err = mIn.read(&tr, sizeof(tr));
 ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
 if (err != NO_ERROR) goto finish;

 if (reply) {
 if ((tr.flags & TF_STATUS_CODE) == 0) {
 reply->ipcSetDataReference(
 reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
 tr.data_size,
 reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
 tr.offsets_size/sizeof(binder_size_t),
 freeBuffer);
 } else {
 ...
 }
 } else {
 ...
 continue;
 }
 }
 goto finish;
.....
}

也就是执行上面的ipcSetDataReference,可以看一下其源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
 const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{

 freeData(); //初始化Parcel状态

 mData = const_cast<uint8_t*>(data);
 mDataSize = mDataCapacity = dataSize;
 mObjects = const_cast<binder_size_t*>(objects);
 mObjectsSize = mObjectsCapacity = objectsCount;
 mOwner = relFunc;

 binder_size_t minOffset = 0;
 for (size_t i = 0; i < mObjectsSize; i++) {
 binder_size_t offset = mObjects[i];
 if (offset < minOffset) {

 mObjectsSize = 0;
 break;
 }
 const flat_binder_object* flat
 = reinterpret_cast<const flat_binder_object*>(mData + offset);
 uint32_t type = flat->hdr.type;
 if (!(type == BINDER_TYPE_BINDER || type == BINDER_TYPE_HANDLE ||
 type == BINDER_TYPE_FD)) {
 ....
 break;
 }
 minOffset = offset + sizeof(flat_binder_object);
 }
 scanForFds();
}

代码比较简单,主要就是把data传入Parcel中,但是除此之外我们需要关注一下传入的relFunc,传入的方法为freeBuffer,此方法的执行会在下一次调用freeData的时候执行,它的实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
 size_t /*dataSize*/,
 const binder_size_t* /*objects*/,
 size_t /*objectsSize*/)
{
 ALOG_ASSERT(data != NULL, "Called with NULL data");
 if (parcel != nullptr) parcel->closeFileDescriptors();
 IPCThreadState* state = self();
 state->mOut.writeInt32(BC_FREE_BUFFER);
 state->mOut.writePointer((uintptr_t)data);
 state->flushIfNeeded();
}

看到这里,我们知道会调用binder发送这个BC_FREE_BUFFER命令,这样驱动内部会清理内存,这样就完成了Parcel和内存缓冲区的空间清理。

readStrongBinder

readStrongBinder和我们之前看过的writeStrongBinder应该是一个相反的过程,直接看代码:

1
2
3
4
5
status_t Parcel::readStrongBinder(sp<IBinder>* val) const
{
 status_t status = readNullableStrongBinder(val);
 return status;
}

上面的代码会调用readNullableStrongBinder,而其内部又会调用unflattenBinder,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
status_t Parcel::unflattenBinder(sp<IBinder>* out) const
{
 if (isForRpc()) {
 ...
 return finishUnflattenBinder(binder, out);
 }

 const flat_binder_object* flat = readObject(false);

 if (flat) {
 switch (flat->hdr.type) {
 case BINDER_TYPE_BINDER: {
 sp<IBinder> binder =
 sp<IBinder>::fromExisting(reinterpret_cast<IBinder*>(flat->cookie));
 return finishUnflattenBinder(binder, out);
 }
 case BINDER_TYPE_HANDLE: {
 sp<IBinder> binder =
 ProcessState::self()->getStrongProxyForHandle(flat->handle);
 return finishUnflattenBinder(binder, out);
 }
 }
 }
 return BAD_TYPE;
}

其中readObject为从Parcel中读取flat_binder_object对象,当请求的进程和服务在同一个进程时候,这里的type就是BINDER_TYPE_BINDER,当请求的进程和服务不在同一个进程则为BINDER_TYPE_HANDLE,因此我们这里是BINDER_TYPE_HANDLEgetStrongProxyForHandle我们之前在分析获取ServiceManager的时候已经分析过了,只不过那个地方handle为固定的0,而这里则是从驱动中传过来的值,最后我们会拿到一个BpBinder,也就完成了查找的过程。

分析完添加服务,查找服务,一直之前介绍的启动ServiceManager和获取ServiceManager基本上就把Binder除了驱动部分的东西都覆盖了。还剩下应用层应该如何使用Binder以及,我们的匿名binder是怎么查找的,这个留待下次在写。

如果你也对于Android系统源码感兴趣,欢迎与我交流。

看完评论一下吧

Android源码分析:ServiceManager启动代码解析

2024年9月5日 20:41

之前已经分析过获取ServiceManager了,不过那是在使用端,在分析使用ServiceManager去获取服务或者添加服务的时候发现,我使用的Android Studio for Platform默认没有把ServiceManager的源码导入。并且同时我们不知道ServiceManager的服务端是怎么启动,怎么响应的,因此决定还是需要分析一下这块的代码。

首先简单画了一下启动的流程,如下图所示(Entry表示我们的调用入口,也就是后面所说的main函数):

sequenceDiagram
autonumber
Entry->>+ProcessState: initWithDriver
ProcessState->>ProcessState: init
ProcessState-->>-Entry: return ProcessState
Entry->>ServiceManager: new
Entry->>ServiceManager: addService(this)
Entry ->>IPCThreadState: setTheContentObject
Entry->>ProcessState: becomeContextManager
Entry->>Looper:prepare
Entry ->>Looper:addFd
loop 永不退出
Entry ->>Looper:pollAll
end

ServiceManager的启动是在系统启动的时候进行启动的,它的启动是使用Linux系统的服务启动方式进行配置,配置文件在servicemanager.rc当中,配置如下:

service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
onrestart restart apexd
...
task_profiles ServiceCapacityLow
shutdown critical

具体系统如何调用这个Service的我们这里不必关心,我们可以直接来看ServiceManager启动相关的代码,代码在frameworks/native/cmds/servicemanager目录下面。启动的逻辑在main.cppmain方法中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main(int argc, char** argv) {
 ...
 const char* driver = argc == 2 ? argv[1] : "/dev/binder";
 sp<ProcessState> ps = ProcessState::initWithDriver(driver);
 ps->setThreadPoolMaxThreadCount(0); //设置最大线程数,因为serviceManager不使用`startThreadPool`启动线程池,因此设置为0
 ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
 sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
 if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
 LOG(ERROR) << "Could not self register servicemanager";
 }

 IPCThreadState::self()->setTheContextObject(manager);
 ps->becomeContextManager();
 sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 BinderCallback::setupTo(looper);
 ClientCallbackCallback::setupTo(looper, manager);
 while(true) {
 looper->pollAll(-1);
 }
 // 应该不会走到,除非发生了错误
 return EXIT_FAILURE;
}

代码如上,数量不多,并且其中看到了不少熟悉的类名,首先是使用ProcessState类去调用它的initWithDriver,这里最终也会走到init方法,做的事情也是创建ProcessState实例,并且打开Binder驱动,获取驱动的FD,可以参考前面的文章。

第7行代码,我们可以看到,创建了一个ServiceManager对象,这个对象与我们之前分析的ServiceManager是不同的,它是ServiceManager的Binder服务端,这个代码也跟我们的初始化代码在同一个目录。拿到这个manager后面做的第一件事情就是调用addService把自己也加进去,addService的代码后面再来分析。

再看12行代码,setTheContextObject处传入了manager,这里IPCThreadState初始化代码之前也已经分析过,此处略过,这个方法也只是把manager作为它的成员放入,暂时略过,直接看后面的becomeContextManager源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
bool ProcessState::becomeContextManager()
{
 AutoMutex _l(mLock);
 flat_binder_object obj {
 .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
 };
 //与Binder交互,发送命令BINDER_SET_CONTEXT_MGR_EXT,让当前进程成为Binder的上下文管理者
 int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);

 if (result != 0) {
 android_errorWriteLog(0x534e4554, "121035042");

 int unused = 0;
 // 执行失败,则重置该参数
 result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
 }
 ...
 return result == 0;
}

上面的代码就是告诉binder驱动,当前的进程要成为binder的上下文管理者,驱动内部作何处理我们便不再深究。

继续看我们前面的代码,就出现了熟悉的Looper,这个Java层的Looper用法和功能都一样,preapre之后又在死循环中调用pollAll,从而我们的代码就会永远在这里执行而不会退出。但是Looper在这里有什么用处,我们需要看看前面的代码。首先是15行的调用,其源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
 sp<BinderCallback> cb = sp<BinderCallback>::make();

 int binder_fd = -1;
 IPCThreadState::self()->setupPolling(&binder_fd);

 int ret = looper->addFd(binder_fd,
 Looper::POLL_CALLBACK,
 Looper::EVENT_INPUT,
 cb,
 nullptr /*data*/);
 return cb;
}

第5行代码调用的setupPolling代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
status_t IPCThreadState::setupPolling(int* fd)
{
 if (mProcess->mDriverFD < 0) { //当前驱动没有成功打开时候,直接返回报错
 return -EBADF;
 }

 mOut.writeInt32(BC_ENTER_LOOPER); //写入Binder调用命令
 flushCommands();
 *fd = mProcess->mDriverFD; //保存Binder驱动的文件描述符到fd当中
 return 0;
}

void IPCThreadState::flushCommands()
{
 if (mProcess->mDriverFD < 0)
 return;
 talkWithDriver(false); //与驱动进行交互
 if (mOut.dataSize() > 0) { //二次确认,未成功则继续交互
 talkWithDriver(false);
 }
}

可以看到上面的代码主要是与Binder驱动交互,并且执行命令进入Binder的循环,且拿到binder驱动的文件描述符,其中talkWithDriver为与binder交互的具体流程,后续在介绍其代码。

拿到binder驱动的文件描述符后执行Looper的addFd方法,最终执行的方法代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
 ...

 { // 此代码段上锁
 AutoMutex _l(mLock);

 if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;
 const SequenceNumber seq = mNextRequestSeq++;

 Request request;
 request.fd = fd;
 request.ident = ident;
 request.events = events;
 request.callback = callback;
 request.data = data;

 epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
 auto seq_it = mSequenceNumberByFd.find(fd);
 if (seq_it == mSequenceNumberByFd.end()) { //列表中不存在该fd
 int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);
 if (epollResult < 0) {
 ...
 return -1;
 }
 mRequests.emplace(seq, request);
 mSequenceNumberByFd.emplace(fd, seq);
 } else {
 int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
 if (epollResult < 0) {
 ...
 return -1;
 }
 const SequenceNumber oldSeq = seq_it->second;
 mRequests.erase(oldSeq);
 mRequests.emplace(seq, request);
 seq_it->second = seq;
 }
 } // release lock
 return 1;
}

上面的代码使用我们的fd创建了epoll_event,并且调用系统调用epoll_ctl来进行注册,只是代码中判断了fd在不再mSequenceNumberByFd当中,在的话使用的是EPOLL_CTL_ADD,不在则使用EPOLL_CTL_MOD。 这里我们需要了解一下epoll,它是linux中的一种高效、可扩展的I/O时间通知机制,而我们这里做的就是监听Binder驱动的FD,当Binder驱动中有变化通知到epoll的文件描述符,也就是我们这里的Looper的回调就可以收到,具体监听的事件为Looper::EVENT_INPUT,从而也就会执行BinderCallbackhandlePolledCommands方法,这个我们留到后面再分析。

看到这,我们可以再看一下main方法的第16行代码,调用的ClientCallbackCallback.setupTo方法,这个类也同样是一个LooperCallback的子类,其代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
 sp<ClientCallbackCallback> cb = sp<ClientCallbackCallback>::make(manager);

 int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);

 itimerspec timespec {
 .it_interval = {
 .tv_sec = 5,
 .tv_nsec = 0,
 },
 .it_value = {
 .tv_sec = 5,
 .tv_nsec = 0,
 },
 };

 int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, &timespec, nullptr);

 int addRes = looper->addFd(fdTimer,
 Looper::POLL_CALLBACK,
 Looper::EVENT_INPUT,
 cb,
 nullptr);

 return cb;
}

这里addFd的代码和之前的一样,但是前面这个timerfd令人感到疑惑,查询一番之后才知道,这里是linux中的定时机制,timerfd为一个基于文件描述符的定时接口,我们这里的代码则是每个5秒钟触发一次,也就是说每隔5秒就会执行一次这个对象的handleEvent方法。

当有客户端请求添加Service或者查询Service等操作的时候,BinderCallbak的handlePolledCommands就会执行去处理,内部会调用如下代码:

1
IPCThreadState::self()->handlePolledCommands();

上面的代码就是会去读取binder传过来的数据,进行处理,具体内容留到后面再分析。

看完评论一下吧

Android源码分析:Binder概述与ServiceManager获取

2024年9月4日 19:09

阅读Android系统源码,Binder是绕不过去的东西,前面看ContentProvider,Activity都有Binder的身影,因此决定还是先把Binder的部分看一看。本文主要简单介绍一下Binder的历史和它的基本架构,介绍Binder的ServiceManager我们在使用的时候如何去拿到它,同时推荐一些Binder的学习资料。

Binder简介

对于普通的Android的应用开发来说,进程的概念是被弱化的。这得益于系统已经帮助我们把Activity,ContentProvider,Broadcast,Service等涉及到跨进程的组件做了很好的封装。我们知道Android也是基于Linux进行开发的,那比如存在跨进程,也就必然存在跨进程通讯。Linux当中跨进程通讯常常使用共享内存、信号量、管道等方式,不过Android中为了安全和使用的便利性,则大部分地方都是使用了Binder。

Binder并不是新提出来的一套跨进程通信机制,它是基于OpenBinder实现的。Binder最早是Be公司开发的, George Hoffman需要一种机制让Be的互联网设备的Javascript UI层与地秤系统服务发生交互,边开发了Binder。后来Be公司的工程师加入了PalmSource开发Palm OS,再后来加入Android,Binder也一直被他们采用,并且也在一直演化,对这段历史感兴趣的话,可以看看《安卓传奇:Android缔造团队回忆录》这本书。开源的OpenBinder是可以工作在Linux内核中的,在2015年已经被合并到Linux 内核3.19版本当中,不仅仅Android,华为的鸿蒙系统当中也在使用Binder。

Binder基本架构

Android中的Binder包括Binder驱动,ServiceManager,Binder服务,他们的关系大概如上图所示。Binder驱动位于Linux内核层,它主要用于实现多个进程之间的通信。ServiceManager位于Android Framework层,用于Binder服务的注册和查找,相当于网络服务中的DNS。而Binder服务的Server端和Client端就是典型的C/S架构,它们通过Binder驱动来进行交互。Android中有两种Binder服务,一种是类似于AMS,PMS这种的系统服务,它们是有名称的服务,注册在ServiceManager当中,Client可以通过名称查找到他们进行使用。还存在另一种匿名Binder服务,比如我们自己通过AIDL创建的,这种我们会直接通过其他的Binder服务把Binder引用传递到客户端,从而双方可以进行通讯。

Binder驱动的源码是在Linux当中的,暂时先不关注了。我这里主要会去看Android Framework层当中Binder相关的代码。ServiceManager本身也是一个Binder,它的ID为0,因此可以很简单的拿到,它的初始化我就不关注了。首先会去关注我们在应用层如何去拿到ServiceManager,因为只有拿到它才能够使用它去注册Binder和获取Binder。其实我们再去看看ServiceManger的addService如何注册一个Binder服务,以及getService 如何获取一个Binder服务。这些看完之后,我们也就知道了Binder的完整运行过程,因为addServicegetService本身也是binder调用,其中我们也会分析Framwork调用kernel相关的代码。最后我们再看看匿名Binder,AIDL,这样差不多就可以对于Framework层的Binder有了全面的了解。

获取ServiceManager

ServiceManager本身是存在一个单独的进程的,并且是在系统启动的时候就启动了它。而我们在其他进程想要通过它来注册服务或者获取服务,就需要首先拿到它的Ibinder对象。通常会用如下的方式获取:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private static IServiceManager getIServiceManager() {
 if (sServiceManager != null) {
 return sServiceManager;
 }

 // Find the service manager 
 sServiceManager = ServiceManagerNative
 .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
 return sServiceManager;
}

上面的代码首先会拿本地的缓存,拿不到才会真正调用获取ServiceManager的步骤,我们先看看这个BinderInternal.getContextObject()方法,它是一个native方法,它的实现在base/core/jni/android_util_Binder.cpp当中,代码如下:

1
2
3
4
5
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
 sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
 return javaObjectForIBinder(env, b);
}

首先调用ProcessState::self()来拿到ProcessState实例,它内部会执行如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{

 if (driver == nullptr) {
 std::lock_guard<std::mutex> l(gProcessMutex);
 if (gProcess) {
 verifyNotForked(gProcess->mForked);
 }
 return gProcess;
 }

 [[clang::no_destroy]] static std::once_flag gProcessOnce;
 std::call_once(gProcessOnce, [&](){
 if (access(driver, R_OK) == -1) {
 driver = "/dev/binder";
 }

 int ret = pthread_atfork(ProcessState::onFork, ProcessState::parentPostFork,
 ProcessState::childPostFork); //注册fork进程的监听
 LOG_ALWAYS_FATAL_IF(ret != 0, "pthread_atfork error %s", strerror(ret));

 std::lock_guard<std::mutex> l(gProcessMutex);
 gProcess = sp<ProcessState>::make(driver); //智能指针初始化
 });

 if (requireDefault) {
 ...
 }

 verifyNotForked(gProcess->mForked);
 return gProcess;
}

上面的代码传入的参数driver值为/dev/binder也就是binder驱动的地址,requireDefault为false。上面的代码中的std:call_once方法为android的libc所提供,就是保证下面的代码段只会执行一次,这个实现也就是为了实现单例,和Java代码中的其实是差不多的。代码中的make方法内部实际会调用ProcessState的构造方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ProcessState::ProcessState(const char* driver)
 : mDriverName(String8(driver)),
 mDriverFD(-1),
 mVMStart(MAP_FAILED),
 mThreadCountLock(PTHREAD_MUTEX_INITIALIZER),
 mThreadCountDecrement(PTHREAD_COND_INITIALIZER),
 mExecutingThreadsCount(0),
 mWaitingForThreads(0),
 mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
 mStarvationStartTimeMs(0),
 mForked(false),
 mThreadPoolStarted(false),
 mThreadPoolSeq(1),
 mCallRestriction(CallRestriction::NONE) {
 base::Result<int> opened = open_driver(driver);

 if (opened.ok()) {
 mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, opened.value(), 0);
 if (mVMStart == MAP_FAILED) {
 close(opened.value()); //mmap失败,关闭binder文件描述符
 opened = base::Error()
 << "Using " << driver << " failed: unable to mmap transaction memory.";
 mDriverName.clear();
 }
 }
 verifyNotForked(gProcess->mForked); //检查当前的实例不是fork之后的只进程的实例否则报错
 if (opened.ok()) {
 mDriverFD = opened.value(); //记录binder的文件描述符
 }
}

open_driver内部就是调用linux的系统调用open打开binder驱动,并通过ioctl获取驱动打开状态以及进行驱动的一些设置如最大线程数等,这里就查看相关代码了。

打开驱动后又会调用mmap把进行内存映射并保存内存指针到mVMStart上,其中内存映射的大小为BINDER_VM_SIZE,定义如下:

1
2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define _SC_PAGE_SIZE 0x0028

其中sysconf(_SC_PAGE_SIZE)之前的值为4k,最新的Android 15改成了16K,那我们这里仍然以来版本计算,可以得到值为1016Kb,这也就是我们使用Binder交互时候数据传输的限制。

这里我们拿到了binder的文件描述符,也完成了内存映射,也就完成了ProcessState的初始化。ProcessState这个对象如它的名字,在每个进程当中只会有一个实例。

有了实例我们又可以继续看getContextObject,主要代码如下:

1
2
3
4
5
6
7
8
9
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
 sp<IBinder> context = getStrongProxyForHandle(0);

 if (context) {
 internal::Stability::markCompilationUnit(context.get()); //更新Binder的Stability,展示可以跳过
 }
 return context;
}

上面主要关注getStrongProxyForHandle(0),这里传入的id为0,也就是专属于ServiceManager的,此方法代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
 const size_t N=mHandleToObject.size();
 if (N <= (size_t)handle) {
 handle_entry e;
 e.binder = nullptr;
 e.refs = nullptr;
 status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
 if (err < NO_ERROR) return nullptr;
 }
 return &mHandleToObject.editItemAt(handle);
}


sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
 sp<IBinder> result;

 AutoMutex _l(mLock);

 handle_entry* e = lookupHandleLocked(handle);

 if (e != nullptr) {
 IBinder* b = e->binder;
 if (b == nullptr || !e->refs->attemptIncWeak(this)) {
 if (handle == 0) {
 IPCThreadState* ipc = IPCThreadState::self();
 CallRestriction originalCallRestriction = ipc->getCallRestriction(); //获取当前的调用限制
 ipc->setCallRestriction(CallRestriction::NONE); //设置限制为空

 Parcel data;
 status_t status = ipc->transact(
 0, IBinder::PING_TRANSACTION, data, nullptr, 0); //调用ping,获取当前Binder的状态

 ipc->setCallRestriction(originalCallRestriction); //恢复原先的限制

 if (status == DEAD_OBJECT)
 return nullptr;
 }

 sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
 e->binder = b.get();
 if (b) e->refs = b->getWeakRefs();
 result = b;
 } else {
 result.force_set(b);
 e->refs->decWeak(this);
 }
 }

 return result;
}


struct handle_entry {
 IBinder* binder;
 RefBase::weakref_type* refs;
};

Vector<handle_entry> mHandleToObject;

handle_entry为结构提,其中存放了IBinderrefs,refs为一个弱引用,用于记录Binder的使用数量,这些entry有存放在动态数组mHandleToObject当中。 查找过程很简单,就是数组中有则返回,无则插入一条。对于ServiceManager,此处会调用IPCThreadState的相关方法,首先看看它的self方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
IPCThreadState* IPCThreadState::self()
{
 if (gHaveTLS.load(std::memory_order_acquire)) {
restart:
 const pthread_key_t k = gTLS;
 IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
 if (st) return st;
 return new IPCThreadState;
 }

 // Racey, heuristic test for simultaneous shutdown.
 if (gShutdown.load(std::memory_order_relaxed)) {
 ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
 return nullptr;
 }

 pthread_mutex_lock(&gTLSMutex);
 if (!gHaveTLS.load(std::memory_order_relaxed)) {
 int key_create_value = pthread_key_create(&gTLS, threadDestructor);
 if (key_create_value != 0) {
 pthread_mutex_unlock(&gTLSMutex);
 ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
 strerror(key_create_value));
 return nullptr;
 }
 gHaveTLS.store(true, std::memory_order_release);
 }
 pthread_mutex_unlock(&gTLSMutex);
 goto restart;
}

这里的gHaveTLS类型为atomic<bool>和java中的AtomicBoolean一样都是原子类型安全的Boolean,这里的TLS不是https中我们说的那个TLS而是表示Thread Local Storage,这里我们就可以明白,此处我们是把IPCThreadState存放在Thread Local中,从而保证每一个线程拥有一个IPCThreadState对象,这个类的构造函数如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
IPCThreadState::IPCThreadState()
 : mProcess(ProcessState::self()),
 mServingStackPointer(nullptr),
 mServingStackPointerGuard(nullptr),
 mWorkSource(kUnsetWorkSource),
 mPropagateWorkSource(false),
 mIsLooper(false),
 mIsFlushing(false),
 mStrictModePolicy(0),
 mLastTransactionBinderFlags(0),
 mCallRestriction(mProcess->mCallRestriction) {
 pthread_setspecific(gTLS, this); //key 为gTLS, value为IPCThreadState,存到ThreadLocal中。
 clearCaller();
 mIn.setDataCapacity(256);
 mOut.setDataCapacity(256);
}

void IPCThreadState::clearCaller()
{
 mCallingPid = getpid();
 mCallingSid = nullptr; // expensive to lookup
 mCallingUid = getuid();
}

构造方法中除了设置mInmOut这两个Parcel外,就是设置Callinguidpid为当前调用进程的值。

回到getStrongProxyForHandle方法,检查binder状态的代码看我的注释就好,可以继续看第41行,它内部调用了如下代码:

1
static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }

而这个create方法内部也主要调用了如下代码:

1
return sp<BpBinder>::make(BinderHandle{handle}, trackedUid);

这里使用了强引用指针,我们解析以下实际上是调用了如下代码:

1
BpBinder(BinderHandle{handle}, trackedUid);

也就是创建了一个BpBinder,在它的构造方法中会调用如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
IPCThreadState::self()->incWeakHandle(this->binderHandle(), this);



void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy)
{
 LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
 mOut.writeInt32(BC_INCREFS);
 mOut.writeInt32(handle);
 if (!flushIfNeeded()) {
 // Create a temp reference until the driver has handled this command.
 proxy->getWeakRefs()->incWeak(mProcess.get());
 mPostWriteWeakDerefs.push(proxy->getWeakRefs());
 }
}

上面写入mOut的数据,在将来调用flushCommands的时候会与Binder驱动交互,这个后面再分析。到这里我们就拿到ServiceManager的BpBinder对象了,但是我们现在还是在native层,因此还需要把对象返回到java层,我们这个时候可以看javaObjectForIBinder方法的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
 if (val->checkSubclass(&gBinderOffsets)) {
 // It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
 jobject object = static_cast<JavaBBinder*>(val.get())->object();
 return object;
 }

 BinderProxyNativeData* nativeData = new BinderProxyNativeData();
 nativeData->mOrgue = new DeathRecipientList;
 nativeData->mObject = val;

 jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
 gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
 if (env->ExceptionCheck()) {
 // In the exception case, getInstance still took ownership of nativeData.
 return NULL;
 }
 BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
 if (actualNativeData == nativeData) {
 // Created a new Proxy
 uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
 uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
 ....
 } else {
 delete nativeData;
 }

 return object;
}

上面的代码首先去看看我们现在的指针中的类是否为Java层Binder类的子类,这种情况在binder由ibinderForJavaObject创建,我们这里不是。因此会使用下面的代码,这里gBinderProxyOffsets的相关值如下:

1
2
3
4
5
6
7
8
9
const char* const kBinderProxyPathName = "android/os/BinderProxy";
jclass clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
 "(JJ)Landroid/os/BinderProxy;");
gBinderProxyOffsets.mSendDeathNotice =
 GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
 "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");

也就是说这里会调用BinderProxygetInstance方法来创建BinderProxy实例,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private static BinderProxy getInstance(long nativeData, long iBinder) {
 BinderProxy result;
 synchronized (sProxyMap) {
 try {
 result = sProxyMap.get(iBinder);
 if (result != null) {
 return result;
 }
 result = new BinderProxy(nativeData);
 } catch (Throwable e) {
NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer, nativeData);
 throw e;
 }
 NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
 sProxyMap.set(iBinder, result);
 }
 return result;
}

主要就是把nativeData放到BinderProxy对象当中,并且用iBinder做为key放到缓存map当中去。到这里native层的代码就全部分析完了。可以继续回到java层的代码。接下来就是调用ServiceManagerNative.asInterface方法,代码如下:

1
2
3
4
5
6
public static IServiceManager asInterface(IBinder obj) {
 if (obj == null) {
 return null;
 }
 return new ServiceManagerProxy(obj);
}

其中就是用ServiceManagerProxy对我们刚刚拿到的BinderProxy进行代理。这样便完成了获取ServiceManager的整个流程。

获取ServiceManager的时序图如下所示:

sequenceDiagram
ServiceManager->>BinderInternal: getContextObject()
BinderInternal->>ProcessState: self()
ProcessState->>ProcessState: getContextObject()
ProcessState->>ProcessState: getStrongProxyForHandle()
ProcessState->>IPCThreadState: self()
IPCThreadState->>IPCThreadState: transact
note over IPCThreadState: PING_TRANSACTION
ProcessState-->>BinderInternal: createBinder
note right of BinderInternal: BpBinder
BinderInternal-->>ServiceManager: javaObjectForIBinder
note right of ServiceManager: BinderProxy
ServiceManager-->>ServiceManager: asInterface
note right of ServiceManager: ServiceManagerProxy

我们现在所分析的流程,本质上还是客户端去获取一个Binder的流程,当然这个binder比较特殊,它直接写死了id为0。可以再回顾一下刚刚涉及到的类。 首先是ProcessState,每个进程都会有一个它的实例,它用于维护打开binder驱动的文件描述符、维护binder线程池以及创建IPCThreadState等。IPCThreadState则用于具体的Binder连接,它会通过ThreadLocal依附于线程,与Binder驱动交互的相关代码都在它的内部。客户端在native端的binder对象为BpBinder,在java端的对象则为BinderProxy。

Binder学习资料

我的文章只会介绍Android Framework层Binder相关的知识,Binder驱动是在Kernel当中的,我不会涉及。另外ServiceManager也是一个系统的守护进程,系统启动的时候也就会启动,我可能也不会分析了。因此推荐以下资料,方便大家在学习Binder,同时对于我没有涉及到的部分也可以参考。

  1. Binder学习指南 这个介绍的还比较通俗易懂
  2. Gityuan Binder系列详解基于Android 6.0,内容详细,从驱动到应用层全部都有讲解
  3. Android深入浅出之Binder机制 邓平凡老师的讲解,可以大概弄清楚binder的机制。

到这里分析完这个流程,我们后面就可以分析addService流程了,待到下次文章继续分享。相互交流才能更好的提高,欢迎读者朋友评论交流。

看完评论一下吧

Android源码分析: ContentProvider查询以及数据变化监听分析

2024年8月29日 18:51

之前已经分析了启动应用安装ContentProvider,使用时获取ContentProvider,我们这里再分析一下使用ContentProvider查询数据已经监听ContentProvider数据变化的情况。

查询数据

上次的文章已经介绍了使用query的方法,并且已经介绍完了通过acquireProvider获取到ContentProvider,如果是是本地应用的话拿到的是Transport对象,如果是查询其他应用(不严谨的说法,其他应用也要排查userId不同,且不共享签名),则拿到的是ContentProviderProxy,这里我们要分析的查询是其他应用的情况,因此我们需要关注ContentProviderProxyquery方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
 @Nullable String[] projection, @Nullable Bundle queryArgs,
 @Nullable ICancellationSignal cancellationSignal)
 throws RemoteException {
 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
 Parcel data = Parcel.obtain();
 Parcel reply = Parcel.obtain();
 try {
 data.writeInterfaceToken(IContentProvider.descriptor);

 attributionSource.writeToParcel(data, 0);
 url.writeToParcel(data, 0);
 int length = 0;
 if (projection != null) {
 length = projection.length;
 }
 data.writeInt(length);
 for (int i = 0; i < length; i++) {
 data.writeString(projection[i]);
 }
 data.writeBundle(queryArgs);
 data.writeStrongBinder(adaptor.getObserver().asBinder());
 data.writeStrongBinder(
 cancellationSignal != null ? cancellationSignal.asBinder() : null);

 mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);

 DatabaseUtils.readExceptionFromParcel(reply);

 if (reply.readInt() != 0) {
 BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
 Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);
 adaptor.initialize(d);
 } else {
 adaptor.close();
 adaptor = null;
 }
 return adaptor;
 } catch (RemoteException ex) {
 adaptor.close();
 throw ex;
 } catch (RuntimeException ex) {
 adaptor.close();
 throw ex;
 } finally {
 data.recycle();
 reply.recycle();
 }
}

这个代码比较简单,把需要查询的条件写入到Parcel中,然后通过mRemote进行binder调用,在reply中拿到远端执行的结果。如果执行成功了,则通过BulkCursorDescriptor来读取reply中的数据,主要是拿到了其中IBulkCursor的Binder对象和CursorWindow这个对象。在查询的流程中会涉及到很多的类,我这里画了使用端和服务端会使用到的Cursor所涉及到相关类和接口。

其中BulkCursorToCursorAdapter为客户端使用,用于读取服务端通过binder传过来的数据,其中的封装和使用,我们后面还会继续看到。关于服务端的我们先继续往后看代码,随后会涉及到相关的类。

可以看看Provider服务端是如何把这些东西放到reply中的。我们这个时候可以看一下ContentProviderNativeonTransactQUERY_TRANSACTION的这一分支:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
data.enforceInterface(IContentProvider.descriptor);

AttributionSource attributionSource = AttributionSource.CREATOR
 .createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);

// String[] projection 
int num = data.readInt();
String[] projection = null;
if (num > 0) {
 projection = new String[num];
 for (int i = 0; i < num; i++) {
 projection[i] = data.readString();
 }
}

Bundle queryArgs = data.readBundle();
IContentObserver observer = IContentObserver.Stub.asInterface(
 data.readStrongBinder());
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
 data.readStrongBinder());

这是其中的第一部分代码,就是把binder传过来的查询需要的数据进行反序列化。

1
2
Cursor cursor = query(attributionSource, url, projection, queryArgs,
 cancellationSignal);

第二部分为调用query进行查询,我们知道在数据提供端,其实是Transport,可以看看它的query方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
 @Nullable String[] projection, @Nullable Bundle queryArgs,
 @Nullable ICancellationSignal cancellationSignal) {
 uri = validateIncomingUri(uri);
 uri = maybeGetUriWithoutUserId(uri);
 if (enforceReadPermission(attributionSource, uri)
 != PermissionChecker.PERMISSION_GRANTED) {
 if (projection != null) {
 return new MatrixCursor(projection, 0);
 }

 Cursor cursor;
 final AttributionSource original = setCallingAttributionSource(
 attributionSource);
 try {
 cursor = mInterface.query(
 uri, projection, queryArgs,
 CancellationSignal.fromTransport(cancellationSignal));
 } catch (RemoteException e) {
 throw e.rethrowAsRuntimeException();
 } finally {
 setCallingAttributionSource(original);
 }
 if (cursor == null) {
 return null;
 }

 // Return an empty cursor for all columns. 
 return new MatrixCursor(cursor.getColumnNames(), 0);
 }
 traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
 final AttributionSource original = setCallingAttributionSource(
 attributionSource);
 try {
 return mInterface.query(
 uri, projection, queryArgs,
 CancellationSignal.fromTransport(cancellationSignal));
 } catch (RemoteException e) {
 throw e.rethrowAsRuntimeException();
 } finally {
 setCallingAttributionSource(original);
 Trace.traceEnd(TRACE_TAG_DATABASE);
 }
}

其中的代码也是比较简单的,首先调用enforceReadPermission检查是否有使用这个ContentProvider的权限,如果有权限则调用mInterface.query,我们看源码就知道,这个mInterface也就是一个ContentProvider,也就是我们开发过程实现的那个ContentProvider,query方法也就是我们自己的实现。

我们先不着急分析后面的代码,我们前面说过如果是本进程的ContentProvider查询会直接调用Transportquery方法,那么就不存在binder调用,而是直接调用了我们所实现的query。这个实现还是很妙的,值得我们学习。对于我们跨进程的调用,还需要看ContentProviderNativeonTransact后面的代码,也就是我们要说的第三部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (cursor != null) {
 CursorToBulkCursorAdaptor adaptor = null;

 try {
 adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
 getProviderName());
 cursor = null;

 BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
 adaptor = null;

 reply.writeNoException();
 reply.writeInt(1);
 d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
 } finally {
 // Close cursor if an exception was thrown while constructing the adaptor. 
 if (adaptor != null) {
 adaptor.close();
 }
 if (cursor != null) {
 cursor.close();
 }
 }
} else {
 reply.writeNoException();
 reply.writeInt(0);
}

这里在拿到数据的时候,通过CursorToBulkCursorAdapter把刚刚查询到Cursor进行了包装,并且通过BulkCursorDescriptor写入到reply中。这样我们刚刚调用端就可以拿到了。 我们看CursorToBulkCursorAdapter可以看到,它的内部又用CrossProcessCursorWrapper来对Cursor进行了封装。

我们已经完成查询,并且获取到Cursor的封装,接下来我们就可以看一下数据的读取了。通过我们前面的Cursor的各个相关类的关系图,我们知道在客户端我们所拿到的是BulkCursorToCursorAdapter,它的初始化代码如下:

1
2
3
4
5
6
7
8
9
public void initialize(BulkCursorDescriptor d) {
 mBulkCursor = d.cursor;
 mColumns = d.columnNames;
 mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
 mCount = d.count;
 if (d.window != null) {
 setWindow(d.window);
 }
}

以下为服务端和客户端交互的流程时序图,通过这个图我们可以具体看到查询过程服务端和客户端交互,以及两边封装的类:

sequenceDiagram
App->>ContentProviderProxy: query
ContentProviderProxy->>+Transport(Server): Binder(QUERY_TRANSACTION)
Transport(Server)->>+ContentProvider: query
ContentProvider -->>- Transport(Server): return query result
note right of Transport(Server): Cursor
Transport(Server) -->>- ContentProviderProxy: Binder(write reply)
note left of Transport(Server): CursorToBulkCursorAdaptor
ContentProviderProxy --> App: return query result
note left of ContentProviderProxy: BulkCursorToCursorAdaptor

服务端传输数据到调用端

可以知道我们主要从服务端拿到两个东西,一个是BulkCursor它是后面我们的数据移动的Binder操作类,CursorWindow则用来存放当前位置的数据。当我们调用CursormoveToNext的时候,就会调用BulkCursorToCursorAdapteronMove方法,进而又会通过binder调用CursorToBulkCursorAdapteronMove方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public boolean onMove(int oldPosition, int newPosition) {
 throwIfCursorIsClosed();

 try {
 if (mWindow == null
 || newPosition < mWindow.getStartPosition()
 || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {
 setWindow(mBulkCursor.getWindow(newPosition));
 } else if (mWantsAllOnMoveCalls) {
 mBulkCursor.onMove(newPosition);
 }
 } catch (RemoteException ex) {
 return false;
 }

 if (mWindow == null) {
 return false;
 }

 return true;
}

当window还没有初始化的时候,会调用setWindowsetWindow很简单,但是mBulkCursor.getWindow却不简单:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public CursorWindow getWindow(int position) {
 synchronized (mLock) {
 throwIfCursorIsClosed();

 if (!mCursor.moveToPosition(position)) {
 closeFilledWindowLocked();
 return null;
 }

 CursorWindow window = mCursor.getWindow();
 if (window != null) {
 closeFilledWindowLocked();
 } else {
 window = mFilledWindow;
 if (window == null) {
 mFilledWindow = new CursorWindow(mProviderName);
 window = mFilledWindow;
 } else if (position < window.getStartPosition()
 || position >= window.getStartPosition() + window.getNumRows()) {
 window.clear();
 }
 mCursor.fillWindow(position, window);
 }

 if (window != null) {
 window.acquireReference();
 }
 return window;
 }
}

我们可以看到此处会调用fillWindow,而此处的mCursorCrossProcessCursorWrapper的实例,fillWindow则会调用如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Override
public void fillWindow(int position, CursorWindow window) {
 if (mCursor instanceof CrossProcessCursor) {
 final CrossProcessCursor crossProcessCursor = (CrossProcessCursor)mCursor;
 crossProcessCursor.fillWindow(position, window);
 return;
 }

 DatabaseUtils.cursorFillWindow(mCursor, position, window);
}

这样就会把每一个postion的数据填充到CursorWindow当中,但是这样会有个问题,为什么客户端能直接拿到呢。我们可以看看CursorWindow的内部。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public CursorWindow(String name, @BytesLong long windowSizeBytes) {
 if (windowSizeBytes < 0) {
 throw new IllegalArgumentException("Window size cannot be less than 0");
 }
 mStartPos = 0;
 mName = name != null && name.length() != 0 ? name : "<unnamed>";
 mWindowPtr = nativeCreate(mName, (int) windowSizeBytes);
 if (mWindowPtr == 0) {
 throw new AssertionError();
 }
 mCloseGuard.open("CursorWindow.close");
}

我们从这里可以看到,CursorWindow保存数据并没有直接放在java中的,而是在natvie中实现的。我们可以在CursorWindow.cpp中找到nativeCreate的实现,我们在其中会发现如下的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//CursorWindow::create
status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) {
 *outWindow = nullptr;

 CursorWindow* window = new CursorWindow();
 if (!window) goto fail;

 window->mName = name;
 window->mSize = std::min(kInlineSize, inflatedSize);
 window->mInflatedSize = inflatedSize;
 window->mData = malloc(window->mSize);
 if (!window->mData) goto fail;
 window->mReadOnly = false;

 window->clear();
 window->updateSlotsData();

 *outWindow = window;
 return OK;

fail:
 LOG(ERROR) << "Failed create";
fail_silent:
 delete window;
 return UNKNOWN_ERROR;
}

但是直接用内存的话,如果我们改变CursorWindow内的数据的时候,在使用端是没办法直接拿到更新的数据的。其实在给插入数据的时候,调用maybeInflate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
status_t CursorWindow::maybeInflate() {
 int ashmemFd = 0;
 void* newData = nullptr;

 // Bail early when we can't expand any further
 if (mReadOnly || mSize == mInflatedSize) {
 return INVALID_OPERATION;
 }

 String8 ashmemName("CursorWindow: ");
 ashmemName.append(mName);

 ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
 ...

 newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
 ...

 {
 // Migrate existing contents into new ashmem region
 uint32_t slotsSize = sizeOfSlots();
 uint32_t newSlotsOffset = mInflatedSize - slotsSize;
 memcpy(static_cast<uint8_t*>(newData),
 static_cast<uint8_t*>(mData), mAllocOffset);
 memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset,
 static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);

 free(mData);
 mAshmemFd = ashmemFd;
 mData = newData;
 mSize = mInflatedSize;
 mSlotsOffset = newSlotsOffset;

 updateSlotsData();
 }

 return OK;
...
}

从代码可以看到,当我们不是只读模式,且size不是和inflateSize相同的时候,会去创建匿名内存,把原来的数据复制的新的匿名内存中去。而会把匿名内存的FD保存到mAshmemFd当中。这样在客户端就可以拿到这个fd,从而可以读取到数据。因为这样做,也只是把fd和CursorWindow的一些基本信息从服务端传到了Client,这样服务端往匿名内存中写数据,客户端也就可以拿到其中的数据了。这样做既可以减少Binder调用的数据量,也可以解决掉Binder传输有1MB的限制。为了验证我们的想法,可以看看代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
status_t CursorWindow::writeToParcel(Parcel* parcel) {
 LOG(DEBUG) << "Writing to parcel: " << this->toString();

 if (parcel->writeString8(mName)) goto fail;
 if (parcel->writeUint32(mNumRows)) goto fail;
 if (parcel->writeUint32(mNumColumns)) goto fail;
 if (mAshmemFd != -1) {
 if (parcel->writeUint32(mSize)) goto fail;
 if (parcel->writeBool(true)) goto fail;
 if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail;
 } else {
 // Since we know we're going to be read-only on the remote side,
 // we can compact ourselves on the wire. size_t slotsSize = sizeOfSlots();
 size_t compactedSize = sizeInUse();
 if (parcel->writeUint32(compactedSize)) goto fail;
 if (parcel->writeBool(false)) goto fail;
 void* dest = parcel->writeInplace(compactedSize);
 if (!dest) goto fail;
 memcpy(static_cast<uint8_t*>(dest),
 static_cast<uint8_t*>(mData), mAllocOffset);
 memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize,
 static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize);
 }
 return OK;

fail:
 LOG(ERROR) << "Failed writeToParcel";
fail_silent:
 return UNKNOWN_ERROR;
}

可以看到在服务端转成Parcel的时候,是写入了name,numRow,numColumn, size, ashMemFd这些,同样,在客户端也会读取这些东西。代码就不贴了。

下面再理一下执行onMoveToNext时候的流程。在客户端的调用如下:

sequenceDiagram
box LightYellow
participant App
participant CursorWrapperInner
participant BulkCursorToCursorAdaptor
participant BulkCursorProxy
end
App->>+CursorWrapperInner: moveToNext
CursorWrapperInner->>BulkCursorToCursorAdaptor: onMove
BulkCursorToCursorAdaptor->>BulkCursorProxy: onMove
BulkCursorProxy->>Remote(CursorToBulkCursorAdapter): binder(ON_MOVE_TRANSACTION)
CursorWrapperInner-->>-App: finishMoveToNext

服务端的调用如下,前面的onMove调用在使用SQLiteCursor的时候会有一些不同,这里以读取SQLite数据库为例,内容有简化:

sequenceDiagram
(Client)BulkCursorProxy->>CursorToBulkCursorAdapter: binder(ON_MOVE_TRANSACTION)
CursorToBulkCursorAdapter->>CrossProcessCursorWrapper: onMove
CrossProcessCursorWrapper->>SQLiteCursor: onMove
SQLiteCursor->>SQLiteCursor: fillWindow
SQLiteCursor->>SQLiteConnection: executeForCursorWindow
SQLiteConnection->>CursorWindow: putXX
note right of SQLiteConnection: native 写入数据到CursorWindow
box LightGreen
participant CursorToBulkCursorAdapter
participant CrossProcessCursorWrapper
participant SQLiteCursor
participant SQLiteConnection
participant CursorWindow
end

ContentObserver监听的注册

当我们想要监听一个ContentProvider的变化时,可以按照如下的方法创建一个ContentObserver,并调用registerContentObserver来注册监听,通过传入的Uri来设置指定的数据源,通过Uri的path可以设置监听指定数据源中的某一部分数据的变化。

1
2
3
4
5
6
7
val contentObserver = object: ContentObserver(Handler.getMain()) {
 override fun onChange(selfChange: Boolean) {
 super.onChange(selfChange)
 //do something while receive onChange
 }
}
contentResolver.registerContentObserver(Uri.parse("content://sms"), true, contentObserver)

我们继承的这个ContentObserver有一个内部类Transport,它实现了 IContentObserver.Stub, 这个和ContentProvider的内部类实现类似,也是实现了binder的数据交互。registerContentObserver方法也在ContentResolver中,代码如下:

1
2
3
4
5
6
7
8
9
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
 ContentObserver observer, @UserIdInt int userHandle) {
 try {
 getContentService().registerContentObserver(uri, notifyForDescendents,
 observer.getContentObserver(), userHandle, mTargetSdkVersion);
 } catch (RemoteException e) {
 throw e.rethrowFromSystemServer();
 }
}

可以看到上面的代码调用了ContentServiceregisterContentObserver方法,这里拿到的是一个binder接口的实现IContentService的代理类,在binder另一端真正执行这个方法的是在ContentService中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
 IContentObserver observer, int userHandle, int targetSdkVersion) {
 final int uid = Binder.getCallingUid();
 final int pid = Binder.getCallingPid();
 userHandle = handleIncomingUser(uri, pid, uid,
 Intent.FLAG_GRANT_READ_URI_PERMISSION, true, userHandle);

 final String msg = LocalServices.getService(ActivityManagerInternal.class)
 .checkContentProviderAccess(uri.getAuthority(), userHandle);
 ...
 synchronized (mRootNode) {
 mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
 uid, pid, userHandle);
 }
}

服务端的主要代码如上,其中传过来的observer为IContentObserver,它就是我们刚刚说到的和Transport相同的接口。其中主要就是调用了mRootNode.addObserverLockedmRootNode是内部类ObserverNode的实例。继续看代码之前先介绍一下这个类,这个类内部又有ObserverEntry内部类,我们的Observer会存放到这个Entry内部。ObserverNode有两个成员mChildrenmObservers,分别表示子一级的ObserverNode和当前路径级的ObserverEntry,从而组成了如下的树状结构。

addObserverLocked这个方法的代码我们也能知晓该树状结构的构成,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private void addObserverLocked(Uri uri, int index, IContentObserver observer,
 boolean notifyForDescendants, Object observersLock,
 int uid, int pid, int userHandle) {
 // If this is the leaf node add the observer 
 if (index == countUriSegments(uri)) {
 mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
 uid, pid, userHandle, uri));
 return;
 }

 // Look to see if the proper child already exists 
 String segment = getUriSegment(uri, index);
 if (segment == null) {
 throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
 }
 int N = mChildren.size();
 for (int i = 0; i < N; i++) {
 ObserverNode node = mChildren.get(i);
 if (node.mName.equals(segment)) {
 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
 observersLock, uid, pid, userHandle);
 return;
 }
 }

 // No child found, create one 
 ObserverNode node = new ObserverNode(segment);
 mChildren.add(node);
 node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
 observersLock, uid, pid, userHandle);
}

这样操作完,也就完成了添加Observer的操作。

数据更新的发布与分发

那数据更新的部分呢,当我们执行了数据整删改之后,需要调用如下代码通知数据变化:

1
getContext().getContentResolver().notifyChange(uri, null);

这个notifyChange方法有几个实现,最终会调用到如下这个:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void notifyChange(@NonNull Uri[] uris, ContentObserver observer, @NotifyFlags int flags,
 @UserIdInt int userHandle) {
 try {
 getContentService().notifyChange(
 uris, observer == null ? null : observer.getContentObserver(),
 observer != null && observer.deliverSelfNotifications(), flags,
 userHandle, mTargetSdkVersion, mContext.getPackageName());
 } catch (RemoteException e) {
 throw e.rethrowFromSystemServer();
 }
}

其中getContentService会获取到IContentService在本地的代理,而最终会通过Binder调用到system_server中的ContentService中的notifyChange方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Override
public void notifyChange(Uri[] uris, IContentObserver observer,
 boolean observerWantsSelfNotifications, int flags, int userId,
 int targetSdkVersion, String callingPackage) {

 final int callingUid = Binder.getCallingUid();
 final int callingPid = Binder.getCallingPid();
 final int callingUserId = UserHandle.getCallingUserId();


 final ObserverCollector collector = new ObserverCollector();



 for (Uri uri : uris) {
 final int resolvedUserId = handleIncomingUser(uri, callingPid, callingUid,
 Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userId);
 final Pair<String, Integer> provider = Pair.create(uri.getAuthority(), resolvedUserId);
 if (!validatedProviders.containsKey(provider)) {
 final String msg = LocalServices.getService(ActivityManagerInternal.class)
 .checkContentProviderAccess(uri.getAuthority(), resolvedUserId);
 if (msg != null) {
 if (targetSdkVersion >= Build.VERSION_CODES.O) {
 throw new SecurityException(msg);
 } else {
 if (msg.startsWith("Failed to find provider")) {
 // Sigh, we need to quietly let apps targeting older API 
 } else {
 Log.w(TAG, "Ignoring notify for " + uri + " from "
 + callingUid + ": " + msg);
 continue;
 }
 }
 }

 // Remember that we've validated this access 
 final String packageName = getProviderPackageName(uri, resolvedUserId);
 validatedProviders.put(provider, packageName);
 }

 synchronized (mRootNode) {
 final int segmentCount = ObserverNode.countUriSegments(uri);
 mRootNode.collectObserversLocked(uri, segmentCount, 0, observer,
 observerWantsSelfNotifications, flags, resolvedUserId, collector);
 }
 }

 final long token = clearCallingIdentity();
 try {
 // Actually dispatch all the notifications we collected 
 collector.dispatch();
 .....
 }
 } finally {
 Binder.restoreCallingIdentity(token);
 }
}

以上代码略有简化,只保留了和ContentObserver通知相关的代码,SyncManager相关的代码未放在这里。这段代码最开始是先遍历传入的Uri列表,对检查对应Uri的ContentProvider是否有权限,如果有权限则会调用collectObserversLocked把满足条件的Observer放到ObserverCollector中去,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//ObserverNode.collectObserversLocked
public void collectObserversLocked(Uri uri, int segmentCount, int index,
 IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
 int targetUserHandle, ObserverCollector collector) {
 String segment = null;
 if (index >= segmentCount) {
 // This is the leaf node, notify all observers 
 collectMyObserversLocked(uri, true, observer, observerWantsSelfNotifications,
 flags, targetUserHandle, collector);
 } else if (index < segmentCount){
 segment = getUriSegment(uri, index);
 // Notify any observers at this level who are interested in descendants 
 collectMyObserversLocked(uri, false, observer, observerWantsSelfNotifications,
 flags, targetUserHandle, collector);
 }

 int N = mChildren.size();
 for (int i = 0; i < N; i++) {
 ObserverNode node = mChildren.get(i);
 if (segment == null || node.mName.equals(segment)) {
 // We found the child, 
 node.collectObserversLocked(uri, segmentCount, index + 1, observer,
 observerWantsSelfNotifications, flags, targetUserHandle, collector);
 if (segment != null) {
 break;
 }
 }
 }
}

传入的segmentCount为Uri的path的数量加上authority的数量,比如content://sms/inbox这个uri它的segmentCount就是2,而从外面传如的index为0。它会先调用collectMyObserversLocked方法来遍历当前Node层级的ObserverEntry,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void collectMyObserversLocked(Uri uri, boolean leaf, IContentObserver observer, boolean observerWantsSelfNotifications, int flags, int targetUserHandle, ObserverCollector collector) {
 int N = mObservers.size();
 IBinder observerBinder = observer == null ? null : observer.asBinder();
 for (int i = 0; i < N; i++) {
 ObserverEntry entry = mObservers.get(i);
 boolean selfChange = (entry.observer.asBinder() == observerBinder);
 if (selfChange && !observerWantsSelfNotifications) {
 continue;
 }

 // Does this observer match the target user? 
 if (targetUserHandle == UserHandle.USER_ALL
 || entry.userHandle == UserHandle.USER_ALL
 || targetUserHandle == entry.userHandle) {
 // Make sure the observer is interested in the notification 
 if (leaf) {
 if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0 && entry.notifyForDescendants) {
 continue;
 }
 } else {
 if (!entry.notifyForDescendants) {
 continue;
 }
 }
 collector.collect(entry.observer, entry.uid, selfChange, uri, flags, targetUserHandle);
 }
 }
}

可以看到以上代码就是遍历我们之前注册observer时候的mObservers列表,分别检查了用户id是否相等,是否满足notifyForDescendants和flag等参数后调用collector.collect方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public void collect(IContentObserver observer, int uid, boolean selfChange, Uri uri,
 int flags, int userId) {
 final Key key = new Key(observer, uid, selfChange, flags, userId);
 List<Uri> value = collected.get(key);
 if (value == null) {
 value = new ArrayList<>();
 collected.put(key, value);
 }
 value.add(uri);
}

Collector中则是以observer,uid,selfChange,flag,userId组合成key,Uri作为value放入collectedmap中。而这些只是完成了一个层级的Observer收集,collectObserversLocked方法中还会遍历mChildren,找到其中的name与segment相同的子Node,再进行收集。收集完成之后,则是调用collector.dispatch(),看名字就知道是去通知对应的Observer,具体实现逻辑如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void dispatch() {
 for (int i = 0; i < collected.size(); i++) {
 final Key key = collected.keyAt(i);
 final List<Uri> value = collected.valueAt(i);

 final Runnable task = () -> {
 try {
 key.observer.onChangeEtc(key.selfChange,
 value.toArray(new Uri[value.size()]), key.flags, key.userId);
 } catch (RemoteException ignored) {
 }
 };
 final boolean noDelay = (key.flags & ContentResolver.NOTIFY_NO_DELAY) != 0;
 final int procState = LocalServices.getService(ActivityManagerInternal.class)
 .getUidProcessState(key.uid);
 if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) {
 task.run();
 } else {
 BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY);
 }
 }
}

这个方法则是遍历collected这个ArrayMap,从每一个key当中取出observer,并使用Runnable封装,在根据flags和当前的进程状态决定是立即通知变化还是延迟通知变化。而这里所调用的onChangeEtc则会通过Binder调用,从而调用到客户端的Observer。

这便完成了ContentProvider内容变化的通知。

总结

本文介绍了使用ContentProvider进行数据的查询、查处来的数据进行窗口移动、注册数据变化监听以及数据变化接受这几块的代码分析,加上前面两篇关于ContentProvider的文章,基本上可以对于ContentProvider整个体系有详细的了解。整删改查这四种操作中,查是比较复杂的,把它看完,增删改这三种流程,想要看明白就会简单很多,因此这里也便不再分析了。

从Android ContentProvider的设计和代码实现中我们可以学到很多东西。其中之一是前面介绍到IContentProvider在自己调用和其他App调用的区别,以及对于代码的巧妙封装,使得代码的实现比较优雅,同时代码量比较少,对于同UID应用来说性能又比较优。另外就是通过CursoWindow的实现,突破Binder数据传输的限制。ObserverNode中使用树来实现了Observer监听。

当然这只是我的一人之言,因为关注点不同,在看代码的过程中还是会有一些细节被我忽略,但是可能对于其他人来说又比较重要的。如果你对ContentProvider也有自己的见解,又或是我有错误的解读,欢迎留言交流。

看完评论一下吧

Android源码分析: 使用场景获取ContentProvider分析

2024年8月27日 10:22

之前已经分析过在应用启动的时候安装ContentProvider的流程了,现在我们再从使用者的角度看看是怎样去拿到ContentProvider的。

在使用ContentProvider的时候,我们通常会使用Context拿到ContentResolver,然后在执行CURD的操作,比如我们要查询手机中的联系人,通常会这样做:

1
2
3
4
5
6
7
8
String[] projection = new String[]    {
 Profile._ID,       
 Profile.DISPLAY_NAME_PRIMARY,       
 Profile.LOOKUP_KEY,       
 Profile.PHOTO_THUMBNAIL_URI    };
Cursor profileCursor = getContentResolver().query(
Profile.CONTENT_URI,
projection , null, null,null);

这里先重点分析一下拿到ContentProvider的过程。首先来看看这个ContentResolver是什么东西。通过源码我们可以看到它是一个抽象类,实现了ContentInterface接口,ContentInterface中则定义了CRUD的相关方法。我们可以在ContextImpl中找到getContentResolver(),通过源码我们知道,实际上我们拿到的是ApplicationContentResolver对象。

这里看完,我们可以继续看query方法,实现在ContentResolver类当中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
IContentProvider unstableProvider = acquireUnstableProvider(uri);
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
 try {
 qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
 queryArgs, remoteCancellationSignal);
 } catch (DeadObjectException e) {
 unstableProviderDied(unstableProvider);
 stableProvider = acquireProvider(uri);
 if (stableProvider == null) {
 return null;
 }
 qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
 queryArgs, remoteCancellationSignal);
 }
 qCursor.getCount();
 final IContentProvider provider = (stableProvider != null) ? stableProvider
 : acquireProvider(uri);
 final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
 stableProvider = null;
 qCursor = null;
 return wrapper;
} catch (RemoteException e) {
 return null;
} finally {
 if (qCursor != null) {
 qCursor.close();
 }
 if (unstableProvider != null) {
 releaseUnstableProvider(unstableProvider);
 }
 if (stableProvider != null) {
 releaseProvider(stableProvider);
 }
}

上面的代码看起来还是比较简单的,首先是是去调用acquireUnstableProvider拿到unstableProvider,通过它去取数据,如果拿不到再去调用acquireProviderstableProvider,最后把stableProvider和数据使用CursorWrappInner包装返回给调用者,在finally中把cursor关掉,把provider给释放掉。

我们先来看看拿stableProvider的逻辑:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public final IContentProvider acquireUnstableProvider(Uri uri) {
 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
 return null;
 }
 String auth = uri.getAuthority();
 if (auth != null) {
 return acquireUnstableProvider(mContext, uri.getAuthority());
 }
 return null;
}

简单说一下,上面首先会判断我们的URL是否为content:开头,因为这是ContentProvider的scheme。之后会到url中拿到authority, autority包括这几个部分:[userinfo@]host[:port] 。最后通过authority去调用ApplicationContentResolver中的同名方法。

1
2
3
4
5
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
 return mMainThread.acquireProvider(c,
 ContentProvider.getAuthorityWithoutUserId(auth),
 resolveUserIdFromAuthority(auth), false);
}

上面的方法会从我们的authority分别拿出userId和host,当然userId有可能是不传的,就会默认使用当前用户。我们继续去看ActivityThread.acquireProvider()代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final IContentProvider acquireProvider(
 Context c, String auth, int userId, boolean stable) {
 final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
 if (provider != null) {
 return provider;
 }
 ContentProviderHolder holder = null;
 final ProviderKey key = getGetProviderKey(auth, userId);
 try {
 synchronized (key) {
 holder = ActivityManager.getService().getContentProvider( getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
 if (holder != null && holder.provider == null && !holder.mLocal) {
 synchronized (key.mLock) {
 if(key.mHolder != null) {
 } else {
 key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS)
 }
 holder = key.mHolder;
 }
 }
 }
 } finally {
 synchronized (key.mLock) {
 key.mHolder = null;
 }
 }
 holder = installProvider(c, holder, holder.info, true, holder.noReleaseNeeded, stable);
 return holder.provider;
}

这里我们有一个参数stable,因此我们前面获取stableProviderunstableProvider都会走到这个方法里面来。 第3行代码,我们首先会到已存在的Provider列表中去拿,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final IContentProvider acquireExistingProvider(
 Context c, String auth, int userId, boolean stable) {
 synchronized (mProviderMap) {
 final ProviderKey key = new ProviderKey(auth, userId);
 final ProviderClientRecord pr = mProviderMap.get(key);
 if (pr == null) {
 return null;
 }

 IContentProvider provider = pr.mProvider;
 IBinder jBinder = provider.asBinder();
 if (!jBinder.isBinderAlive()) {
 //处理Binder不存活的情况
 handleUnstableProviderDiedLocked(jBinder, true);
 return null;
 }

 ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
 if (prc != null) {
 incProviderRefLocked(prc, stable); //增加引用计数
 }
 return provider;
 }
}

可以看到此处为通过auth构建出来的key到mProviderMap中查找ProviderClientRecord,而这个就是我们之前分析安装Provider时候所创建并且放置到这个map中去的。后面会检查Binder是否仍然存活,并返回。 在这里我们需要注意一点,如果安装我们之前分析安装的流程,我们在自己的app里面拿自己的ContentProvider这里是肯定可以拿到的,但是如果是其他的应用提供的ContentProvider这里很显然是拿不到的。因此我们需要继续回到acquireProvider方法去看其他部分的代码。

在第11行中,我们会到AMS中去获取ContentProviderHolder,如果拿到了远端的holder,但是我们本地的ProviderKey中的holder为空,说明我们本地还没有安装这个ContentProvider,需要等待,也就是执行第16行代码进入等待状态。而这个地方的解除等待在ContentProviderHelper类的publishContentProviders方法中,可以去之前分析安装过程的文章最后一部分查看。

而拿到holder之后,最后又去执行了一次installProvider方法,这里的安装跟我们之前的启动App安装是有一些不同的,我们放到后面再来分析。

然而前面的去AMS拿ContentProviderHolder代码我们还没有看,具体代码也仍然在ContentProviderHelper中,现在去看一下它的getContentProviderImpl()方法,内容比较长,先一点一点的贴代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//ContentProviderHelper.java getContentProviderImpl
synchronized (mService) {
 ProcessRecord r = null;
 if (caller != null) {
 r = mService.getRecordForAppLOSP(caller);
 }

 UserManagerService userManagerService = UserManagerService.getInstance();
 if (!isAuthorityRedirectedForCloneProfile(name)
 || !userManagerService.isMediaSharedWithParent(userId)) { //。mediastore需要特殊判断,这里会把那些情况给过滤掉
 cpr = mProviderMap.getProviderByName(name, userId);
 }
 ...

 ProcessRecord dyingProc = null;
 if (cpr != null && cpr.proc != null) {
 providerRunning = !cpr.proc.isKilled(); //检查ContentProvider目标进程是否被杀掉
 if (cpr.proc.isKilled() && cpr.proc.isKilledByAm()) {
 dyingProc = cpr.proc; //如果被杀了或者正在被杀就记录
 }
 }

 if (providerRunning) {
 cpi = cpr.info;
 if (r != null && cpr.canRunHere(r)) {
 checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
 cpr.name.flattenToShortString(), startTime);
 ContentProviderHolder holder = cpr.newHolder(null, true);
 holder.provider = null;
 return holder;
 }
 //PLACEHOLDER1
 }
 //PLACEHOLDER2
}

以上的代码是我们会遇到的第一种情况,首先去拿到进程ProcessRecord,之后根据Provider的authority name和userId到ProviderMap中拿已有的ContentProviderRecord。拿到之后首先检查ContentProvider提供方的进程是否正在运行中,如果在运行中,并且canRunHere检查为true, 就会检查是否有权限来执行,有权限就会创建一个ContentProviderHolder传递出去。 canRunHere所做的判断代码如下:

1
2
3
4
public boolean canRunHere(ProcessRecord app) {
 return (info.multiprocess || info.processName.equals(app.processName))
 && uid == app.info.uid;
}

解释下就是首先判断Provider是否支持多个进程中运行,也就是在Manifest为provider配置了multiprocess=true,另外检查Provider所在进程和当前调用是否为同一个进程,这两者条件满足一个就可以。同时还要满足当前进程的UID和Provider的进程UID相同,这个在两者为同一个应用,或者两者共享签名,或共享UID的情况下满足。这种情况下就可以直接使用ContentProvider。这种情况会创建新的ContentProviderHolder传递到App进程,其中会携带ContentProviderRecord过去。此时我们看到的传到App进程的ContentProviderConnection也是为空,至于这个对象的用处是什么我们后面会分析。同时还会把Holder的成员provider设置为空,这个有什么用呢,可以后面再看installProvider方法。

在这里还有一个检查权限和是否可以联合运行的方法checkAssociationAndPermissionLocked,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
if ((msg = checkContentProviderAssociation(callingApp, callingUid, cpi)) != null) {
 throw new SecurityException("Content provider lookup " + cprName
 + " failed: association not allowed with package " + msg);
}

if ((msg = checkContentProviderPermission(
 cpi, Binder.getCallingPid(), Binder.getCallingUid(), userId, checkUser,
 callingApp != null ? callingApp.toString() : null))
 != null) {
 throw new SecurityException(msg);
}

里面又分别调用了两个方法,第一个用于检查两个进程是否可以联合使用,默认是允许的,除非是系统内置应用或者预装应用会有比较严格的检查,我们这里不必关注。可以去看一下权限检查,这个比较重要:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private String checkContentProviderPermission(ProviderInfo cpi, int callingPid, int callingUid,
 int userId, boolean checkUser, String appName) {
 boolean checkedGrants = false;
 if (checkUser) { //对于普通应用这个值传过来的为true
 int tmpTargetUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
 if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
 //检查是否有临时授权,这个一般是在Manifest中添加<grant-uri-permission>或者android:grantUriPermissions
 if (mService.mUgmInternal.checkAuthorityGrants(
 callingUid, cpi, tmpTargetUserId, checkUser)) {
 return null; //检查通过直接返回成功
 }
 checkedGrants = true;
 }
 userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId,
 false, ActivityManagerInternal.ALLOW_NON_FULL,
 "checkContentProviderPermissionLocked " + cpi.authority, null);
 if (userId != tmpTargetuserId) {
 checkGrants = false;
 }
 }
 if (ActivityManagerService.checkComponentPermission(cpi.readPermission,
 callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
 == PackageManager.PERMISSION_GRANTED) { //检查读权限,授权过返回
 return null;
 }
 if (ActivityManagerService.checkComponentPermission(cpi.writePermission,
 callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
 == PackageManager.PERMISSION_GRANTED) { //写权限检查,授权过则返回成功
 return null;
 }
 PathPermission[] pps = cpi.pathPermissions;
if (pps != null) {
 int i = pps.length;
 while (i > 0) {
 i--;
 PathPermission pp = pps[i];
 String pprperm = pp.getReadPermission();
 if (pprperm != null && ActivityManagerService.checkComponentPermission(pprperm,
 callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
 == PackageManager.PERMISSION_GRANTED) {
 return null;
 }
 String ppwperm = pp.getWritePermission();
 if (ppwperm != null && ActivityManagerService.checkComponentPermission(ppwperm,
 callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
 == PackageManager.PERMISSION_GRANTED) {
 return null;
 }
 }
}

}

关于权限,前面的代码我已经加了相关的注释,我们可以对比官方文档,其中共检查了四种权限,分别是临时授权,路径授权,单独的读写授权和单一读写程序级别的授权。关于权限检查的更多内容,这里我们也先略过。此时我们可以继续回来继续分析getContentProviderImpl方法。我们继续看上面留的PLACEHOLDER 1处的代码:

1
2
3
4
5
6
checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
 cpr.name.flattenToShortString(), startTime);
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
 callingTag, stable, true, startTime, mService.mProcessList,
 expectedUserId);

其中还有一些关于OOM设置的代码这里先跳过了,上面主要的代码也是检查权限以及这个incProviderCountLocked方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private ContentProviderConnection incProviderCountLocked(ProcessRecord r,
 final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
 String callingPackage, String callingTag, boolean stable, boolean updateLru,
 long startTime, ProcessList processList, @UserIdInt int expectedUserId) {
 final ProcessProviderRecord pr = r.mProviders;
 for (int i = 0, size = pr.numberOfProviderConnections(); i < size; i++) {
 ContentProviderConnection conn = pr.getProviderConnectionAt(i);
 if (conn.provider == cpr) {
 conn.incrementCount(stable);
 return conn;
 }
 }

 ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage,
 expectedUserId);
 conn.startAssociationIfNeeded();
 conn.initializeCount(stable);
 cpr.connections.add(conn);
 if (cpr.proc != null) {
 cpr.proc.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
 }
 pr.addProviderConnection(conn);
 mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),
 cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
 if (updateLru && cpr.proc != null
 && r.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
 processList.updateLruProcessLocked(cpr.proc, false, null);
 }
 return conn;
}

这里有不少关于Association相关的代码,而我们的应用一般不会走到这里。我们只需要关注其中创建Connection以及为他创建引用计数。关于它的计数,我们放到最好再看一下。

PLACEHOLDER 2处,首先处理的就是provider为运行的情况,这种情况就会回到Provider的进程去安装ContentProvider,这部分代码我们之前已经分析过了,这里略过。而我们是在使用者进程调用的此处的caller也不为空,再往后,则应该是如下的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mService.grantImplicitAccess(userId, null, callingUid,
 UserHandle.getAppId(cpi.applicationInfo.uid));

if (caller != null) {
 synchronized (cpr) {
 if (cpr.provider == null) {
 if (cpr.launchingApp == null) {
 return null;
 }

 if (conn != null) {
 conn.waiting = true;
 }
 }
 }
 return cpr.newHolder(conn, false);
}

这里可以看到,就是先给调用的uid授权,设置wait 为true,创建一个ContentProviderHolder返回。这里是带着ContentProviderConnectionIContentProvider的。

代码讲解的部分只介绍了我们认为caller不为空的情况,实际上是更加复杂的,这里就把其中的完整流程流程图放在这里,如有需要可参考流程图以及之前的App启动时候的ContentProvider安装一起看。

---
title: getContentProviderImpl流程
---
flowchart TD
A(getContentProviderImpl) --> B(mProviderMap.getProviderByName)
B --> C(providerRunning = !cpr.proc.isKilled)
C --> D{Check providerRunning}
D --> |providerRunning == true|E{cpr.canRunHere}
E --> |No| I{CheckPermission}
E --> |Yes|F{ChecPermission}
F --> |Pass|G((Return local Holder))
F --> |Not Pass|H(Throw Exception)
I --> |Pass|J(incProviderCountLocked)
I --> |Not Pass|H
D --> |No|K(PMS.resolveContentProvider)
K --> L{CheckPermission}
L --> |Not Pass|H
L --> |Pass|M(Generate CPRecord)
M --> A1{cpr.canRunHere}
A1 --> |true|A2((Return local Holder))
A1 --> |False| A3{Process Live}
A3 --> |Process is live|A4(Install Provider)
A3 --> |Not Start Or Die| A5(Start Process)
A4 --> A6(incProviderCountLocked)
A5 --> A6
A6 --> B1(AMS.grantImplictAccess)
J --> B1
B1 --> B2{From customer Call}
B2 --> |Yes|B3((Return Remote Holder))
B2 --> |No|B4{cpr.provider==null}
B4 --> |Yes|B5((cpr.wait))
B5 --> B4
B4 --> |No|B6((Return Remote Holder))

看了这么多,我们就可以继续回去看App进程的代码了。在App进程就是执行我们前面说的installProvider过程。 我们可以继续分析query的过程,看代码我们知道调用的是IContentProvider的query方法,对于同UID的进程,IContentProvider为我们在instalProvider创建的本地的ContentProvider中的mTransport而其他的则是AMS调用带过来的IcontentProvider远端接口,我们这里以非本进程的情况来分析,它的获取是如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//android.content.ContentProviderNative.java
static public IContentProvider asInterface(IBinder obj)
{
 if (obj == null) {
 return null;
 }
 IContentProvider in =
 (IContentProvider)obj.queryLocalInterface(descriptor);
 if (in != null) {
 return in;
 }

 return new ContentProviderProxy(obj);
}

也就是说,如果是相同的UID的进程拿到的为Transport对象,如果是其他的则拿到的是ContentProviderProxy对象。

前面我们还有关于ContentProviderConnection还有很多东西没有介绍,这里继续看一下。首先是incProviderCountLocked方法中所调用的conn.incrementCount(stable)。在我看代码的过程中stable这个变量唯有这里使用了,我们继续看它的源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public int incrementCount(boolean stable) {
 synchronized (mLock) {
 if (stable) {
 mStableCount++;
 mNumStableIncs++;
 } else {
 mUnstableCount++;
 mNumUnstableIncs++;
 }
 return mStableCount + mUnstableCount;
 }
}

可以看到这个类主要记录了Stable和UnStable的调用次数,实际上AMS这一端stable和unstable似乎除了计数之外没有什么区别。但是在客户端installProvider的时候却是有区别的。我们之前分析的启动时候安装的情况stable都是为true,我们可以看看ActivityThread.installProvider如下的代码:

1
2
3
4
5
6
7
8
if (noReleaseNeeded) {
 prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
 prc = stable
 ? new ProviderRefCount(holder, client, 1, 0)
 : new ProviderRefCount(holder, client, 0, 1);
}
mProviderRefCountMap.put(jBinder, prc);

ProviderRefCount用于记录Provider的引用计数,其中用stableCount和unstableCount来计数,当我们不需要释放Provider的时候,两个数字都设置为了1000,当我们是stable的时候只设置stable数为1,unstable数量为0,当为unstable的时候也同理。之前我们是有看到对于已经存在的provider是通过incProviderRefLocked来增加起计数的。那我们有了增加计数,那么使用完之后也应该需要减少计数。在query的finally代码块中有如下代码:

1
2
3
4
5
6
if (unstableProvider != null) {
 releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
 releaseProvider(stableProvider);
}

他们最终调用的为ActivityThread.releaseProvider方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
 if (provider == null) {
 return false;
 }

 IBinder jBinder = provider.asBinder();
 synchronized (mProviderMap) {
 ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
 if (prc == null) {
 // The provider has no ref count, no release is needed. 
 return false;
 }

 boolean lastRef = false;
 if (stable) {
 if (prc.stableCount == 0) {
 return false;
 }
 prc.stableCount -= 1;
 if (prc.stableCount == 0) {
 lastRef = prc.unstableCount == 0;
 try {

 ActivityManager.getService().refContentProvider(
 prc.holder.connection, -1, lastRef ? 1 : 0);
 } catch (RemoteException e) {
 //do nothing content provider object is dead any way 
 }
 }
 } else {
 if (prc.unstableCount == 0) {
 return false;
 }
 prc.unstableCount -= 1;
 if (prc.unstableCount == 0) {
 lastRef = prc.stableCount == 0;
 if (!lastRef) {
 try {

 ActivityManager.getService().refContentProvider(
 prc.holder.connection, 0, -1);
 } catch (RemoteException e) {
 //do nothing content provider object is dead any way 
 }
 }
 }
 }

 return true;
 }
}

代码主要分了两个分支,分别对stable和unstable的情况进行处理,他们都是先把本地对应的ProviderRefCount中的数字减一,但是调用AMS.refContentProvider却不一样,stable count减为0的时候会直接调用,而unstable为0的时候要stableCount不为0才会调用。传递的参数也有区别,代码很简单就不详解了。直接去看ContentProviderHelperrefContentProvider方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
boolean refContentProvider(IBinder connection, int stable, int unstable) {
 ContentProviderConnection conn;
 try {
 conn = (ContentProviderConnection) connection;
 } catch (ClassCastException e) {

 }
 if (conn == null) {
 throw new NullPointerException("connection is null");
 }

 try {
 conn.adjustCounts(stable, unstable);
 return !conn.dead;
 } finally {

 }
}

这里的代码其实比较简单,就是调用ContentProviderConnectionadjustCounts,这个方法的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void adjustCounts(int stableIncrement, int unstableIncrement) {
 synchronized (mLock) {
 if (stableIncrement > 0) {
 mNumStableIncs += stableIncrement;
 }
 final int stable = mStableCount + stableIncrement;
 if (stable < 0) {
 throw new IllegalStateException("stableCount < 0: " + stable);
 }
 if (unstableIncrement > 0) {
 mNumUnstableIncs += unstableIncrement;
 }
 final int unstable = mUnstableCount + unstableIncrement;
 if (unstable < 0) {
 throw new IllegalStateException("unstableCount < 0: " + unstable);
 }
 if ((stable + unstable) <= 0) {
 throw new IllegalStateException("ref counts can't go to zero here: stable="
 + stable + " unstable=" + unstable);
 }
 mStableCount = stable;
 mUnstableCount = unstable;
 }
}

这里就是来根据传过来的参数来调整stableCountunstableCount,也就完成了这几个count的变化。也就是完成了AMS端的减少计数。

到此位置,我们也就拿到了IContentProvider,也就可以使用它提供的CRUD方法,进行数据的增删改查了。至于具体是如何查询数据,如何做到数据的跨进程共享,如何绕过Binder传输限制1MB实现跨进程传输数据,限于篇幅下次再来分析。

看完评论一下吧

Android源码分析: 应用启动安装ContentProvider分析

2024年8月15日 20:02

ContentProvider是Android应用开发的四大组件之一,并且源码相对于其他几个也是比较简单的。因此我们先来看看它的源码。ContentProvider的使用我们会涉及到外部程序调用应用的ContentProvider来查询数据,也有监听数据的变化,以及ContentProvider的安装。我们先来看安装部分的源码。

ContentProvider根据它的名字就知道,他是一个内容提供者,它提供了整删改查的接口,方便Android应用跨应用跨进程的数据共享,在Android系统中,相册,通讯录等等都是通过ContentProvider来共享数据让其他应用可以使用。看源码,我们需要关注两个点,一个是ContentProvider如何安装的,另一个就是当我们发起一个查询的时候,是怎样和内容提供的那个进程进行交互的。

ContentProvider的安装触发通常有两个场景,一是外部程序需要使用ContentProvider的时候,另一个是在应用Application启动的时候,这个一般触发的场景有,启动Service,启动Activity。这所有场景的共同点都是拉起进程,初始化Application。这两大场景来安装ContentProvider除了开始的路径会有差别,后面的部分大致都相等。因此我们这里以启动App同时安装Provider作为分析路径。

ContentProvider相关的类有如下这些:

classDiagram
class IContentProvider
<<interface>> IContentProvider
namespace SYSTEM_SERVER进程 {
class ContentProviderRecord
class ProviderMap
class ProcessProviderRecord
}
namespace 应用进程 {
class ContentProvider
class ContentProviderNative
class Transport
class ProviderClientRecord
class Binder
}
<<abstract>> ContentProvider
class ProviderInfo
<<abstract>> ContentProviderNative
ContentProviderRecord *-- IContentProvider
ContentProviderRecord *-- ProviderInfo
IContentProvider <|.. ContentProviderNative
Binder <|-- ContentProviderNative
ContentProviderNative <|-- Transport
ContentProvider *-- Transport
class ContentProviderHolder
ContentProviderHolder *-- IContentProvider
ProviderClientRecord *-- IContentProvider
ProviderClientRecord *-- ContentProvider
ProviderClientRecord *-- ContentProviderHolder
ContentProviderHolder *-- ProviderInfo
ProviderMap o-- ContentProviderRecord
ProcessProviderRecord o-- ContentProviderRecord

PackageManager解析出来的Provider信息通过ProviderInfo来保存,我们平时创建的ContentProvider它有一个内部类Transport,在它里面实现了Binder的客户端和服务端。通过它的Binder代理,AMS进程能够执行ContentProvider的增删改查,这不是本文的重点,下次再说。在App进程内,除了我们的ContentProvider对象,还会构建ProviderClientRecord对象,ContentProvider和它的binder对象和ProviderInfo信息会存在这个对象中。在服务端也有一个对应的ContentProviderRecord对象,里面存储了IcontentProvider binder对象和ProviderInfo。并且客户端和服务端的这些record都会存到Map中去,这样方便后续调用的时候查找。而构建所有这些record和把他们放入到Map中的过程,其实就是Provider的安装过程。

我们开始看代码,在应用进程创建完成之后,AMS会执行attachApplicationLocked从而来创建App的Application对象,因此我们从这里开始看,而进程的启动可以挖个坑以后再写。最初起点应该在ActivityManagerServiceattachApplicationLocked方法中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode
 ? mCpHelper.generateApplicationProvidersLocked(app)
 : null;
...
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
thread.bindApplication(processName, appInfo,
 app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
 providerList, null, profilerInfo, null, null, null, testMode,
 mBinderTransactionTrackingEnabled, enableTrackAllocation,
 isRestrictedBackupMode || !normalMode, app.isPersistent(),
 new Configuration(app.getWindowProcessController().getConfiguration()),
 app.getCompat(), getCommonServicesLocked(app.isolated),
 mCoreSettingsObserver.getCoreSettingsLocked(),
 buildSerial, autofillOptions, contentCaptureOptions,
 app.getDisabledCompatChanges(), serializedSystemFontMap,
 app.getStartElapsedTime(), app.getStartUptime());

可以看到AMS中首先是通过mCpHelper去生成当前应用的Provider列表,之后调用应用进程的bindApplication的时候再带过去。mCpHelper是一个ContentProviderHelper对象,我们先来看看它的generateApplicationProvidersLocked方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
 final List<ProviderInfo> providers;
 providers = AppGlobals.getPackageManager().queryContentProviders(
 app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS
 | PackageManager.GET_URI_PERMISSION_PATTERNS
 | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null)
 .getList();
 int numProviders = providers.size();
 final ProcessProviderRecord pr = app.mProviders;
 pr.ensureProviderCapacity(numProviders + pr.numberOfProviders());
 for (int i = 0; i < numProviders; i++) {
 // NOTE: keep logic in sync with installEncryptionUnawareProviders 
 ProviderInfo cpi = providers.get(i);
 boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
 cpi.name, cpi.flags);
 if (singleton && app.userId != UserHandle.USER_SYSTEM) {
 // This is a singleton provider, but a user besides the 
 // default user is asking to initialize a process it runs 
 // in... well, no, it doesn't actually run in this process, // it runs in the process of the default user. Get rid of it. 
 providers.remove(i);
 numProviders--;
 i--;
 continue;
 }
 final boolean isInstantApp = cpi.applicationInfo.isInstantApp();
 final boolean splitInstalled = cpi.splitName == null || ArrayUtils.contains(
 cpi.applicationInfo.splitNames, cpi.splitName);
 if (isInstantApp && !splitInstalled) {
 // For instant app, allow provider that is defined in the provided split apk. 
 // Skipping it if the split apk is not installed. 
 providers.remove(i);
 numProviders--;
 i--;
 continue;
 }

 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
 ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, app.userId);
 if (cpr == null) {
 cpr = new ContentProviderRecord(mService, cpi, app.info, comp, singleton);
 mProviderMap.putProviderByClass(comp, cpr);
 }
 pr.installProvider(cpi.name, cpr);
 if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
 app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.longVersionCode,
 mService.mProcessStats);
 }
 mService.notifyPackageUse(cpi.applicationInfo.packageName,
 PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
 }
 return providers.isEmpty() ? null : providers;
}

上面第三行代码为调用PackageManagerService去读取当前应用所有的ContentProvider信息并存储到ProviderInfo列表中,具体代码在ComputerEngine中,这里不分析了。ProviderInfo中存储了每一个ContentProvider的信息,包括它的组件名称,查询的authority,运行的进程,读写的权限等等。这里我们需要注意一下,我们在Manifest文件中声明ContentProvider的时候,是可以指定它所运行的进程的,在这个地方,我们传进来而的app也是一个ProcessRecord进程,它对应的是我们的一个进程的记录而不是app的记录,因此,我们拿到的ProviderInfo也是当前进程需要启动的所有进程。

随后会开启一个循环对每一个Provider做处理,在37行,通过packagename和name组合出ComponentName,这个和其他的构造Activity,Service等的类似。随后会尝试从ProviderMap中获取已经存在的记录,正常情况下这里都是空,如果一个App有多个进程,并且provider可以在多个进程运行,那么这里可能是可以拿到缓存的。

如果没有拿到缓存,我们会开始创建ContentProviderRecord,这是ContentProvider在AMS当中的记录,而它也会放到ProviderMap中,这样下次使用的时候就不需要再次创建了。

43行会调用ProcessProviderRecord的installProvider,这里只是把这条record存放到ProcessRecord的mProviders中去。

我们再回到AMS的代码中去,AMS当中会把我们的List<ProviderInfo>包装成一个ProviderInfoList对象,最后调用到ApplicationThread的bindApplication方法,从而把这些东西传递到App进程。

来到ActivityThread的源码,bindApplication会把AMS带过来的数据封装成AppBindData,通过sendMessage把传递ActivityThread类,并且调用它的handleBindApplication方法,其中我们会看到如下代码:

1
2
3
4
5
6
7
8
9
Application app;
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
if (!data.restrictedBackupMode) {
 if (!ArrayUtils.isEmpty(data.providers)) {
 installContentProviders(app, data.providers);
 }
}

mInstrumentation.callApplicationOnCreate(app);

上面第5行就是去安装ContentProviders。第二行是去创建我们的Application,其中会调用Application的attach方法,第9行是调用Application的onCreate方法,可见ContentProvider的一些方法是在他们两之间执行的,这也是为什么很多SDK通过使用ContentProvider来初始化他们的代码。继续看installContentProviders的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
 ContentProviderHolder cph = installProvider(context, null, cpi,
 false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
 if (cph != null) {
 cph.noReleaseNeeded = true;
 results.add(cph);
}
}
ActivityManager.getService().publishContentProviders(
 getApplicationThread(), results);

上面的代码就是去遍历每一个ContentProvider去安装,我们继续看installProvider的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
 Context c = null;
 ApplicationInfo ai = info.applicationInfo;
 if (context.getPackageName().equals(ai.packageName)) {
 c = context;
 }
 ...
 final java.lang.ClassLoader cl = c.getClassLoader();
 LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
 if (packageInfo == null) {
 // System startup case. 
 packageInfo = getSystemContext().mPackageInfo;
 }
 localProvider = packageInfo.getAppFactory()
 .instantiateProvider(cl, info.name);
 provider = localProvider.getIContentProvider();

 localProvider.attachInfo(c, info);
}
...
synchronized (mProviderMap) {
 IBinder jBinder = provider.asBinder();
 if (localProvider != null) {
 ComponentName cname = new ComponentName(info.packageName, info.name);

 holder = new ContentProviderHolder(info);
 holder.provider = provider;
 holder.noReleaseNeeded = true;
 pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
 mLocalProviders.put(jBinder, pr);
 mLocalProvidersByName.put(cname, pr);

 }

 ...
}

以上代码简化很多,仅保留启动App安装Provider的代码。第10行到第17行的代码,为通过LoadedApk通过反射去创建ContentProvider这个对象,随后通过它拿到IContentProvider对象,也就是它的Binder对象。随后调用attachInfo方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
 mCallingAttributionSource = new ThreadLocal<>();
 mContext = context;
 if (context != null && mTransport != null) {
 mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
 Context.APP_OPS_SERVICE);
 }
 mMyUid = Process.myUid();
 if (info != null) {
 setReadPermission(info.readPermission);
 setWritePermission(info.writePermission);
 setPathPermissions(info.pathPermissions);
 mExported = info.exported;
 mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
 setAuthorities(info.authority);
 }

 ContentProvider.this.onCreate();
}

可以看到其中是为ContentProvider设置一些信息,包括它的Context,以及把Manifest上面设置的一些属性,权限之类的保存到当前这个对象中,最后会调用onCreate方法,这会执行我们重写时写的代码。

再回到installProvider方法,在28行,会创建ContentProviderHolder,随后调用installProviderAuthoritiesLocked把Provider和它所对应的authority对应,并创建ProviderClientRecord,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
 ContentProvider localProvider, ContentProviderHolder holder) {
 final String auths[] = holder.info.authority.split(";");
 final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
 ...
 final ProviderClientRecord pcr = new ProviderClientRecord(
 auths, provider, localProvider, holder);
 for (String auth : auths) {
 final ProviderKey key = new ProviderKey(auth, userId);
 final ProviderClientRecord existing = mProviderMap.get(key);
 if (existing != null) {
 } else {
 mProviderMap.put(key, pcr);
 }
 }
 return pcr;
}

上面的代码很简单,就是创建了ProviderClientRecord,其中保存了auths,我们创建的ContentProvider,以及IContentProvider,ContentProviderHolder,最后把每个authority作为key, ProviderClientRecord作为value,存放到了mProviderMap中。

上面的代码执行完之后,在installProvider中,又分别以binder对象和ComponentName对象为key,ProviderClientRecord对象为value存放到map中。

这一切做完之后,我们还需要回到installContentProviders方法的最后,看看第10行的代码,看代码名称是发布我们的Provider,那我们继续到AMS中去看代码,其中主要调用了如下代码:

1
mCpHelper.publishContentProviders(caller, providers);

继续去ContentHelper中看代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
 synchronized (mService) {
 final ProcessRecord r = mService.getRecordForAppLOSP(caller);
 for (int i = 0, size = providers.size(); i < size; i++) {
 ContentProviderHolder src = providers.get(i);
 ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
 if (dst == null) {
 continue;
 }
 ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
 mProviderMap.putProviderByClass(comp, dst);
 String[] names = dst.info.authority.split(";");
 for (int j = 0; j < names.length; j++) {
 mProviderMap.putProviderByName(names[j], dst);
 }
 r.addPackage(dst.info.applicationInfo.packageName,
 dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
 synchronized (dst) {
 dst.provider = src.provider;
 dst.setProcess(r);
 dst.notifyAll();
 dst.onProviderPublishStatusLocked(true);
 }
 }
 }
}

上面的代码第3行,通过我们传过来的IApplicationThread来获取到我们的进程在AMS当中对应的ProcessRecord。随后会遍历每一个ContentProviderHolder,检查ProcessRecord当中的Record是否都有,随后会把ProcessRecord当中所存储的的CotentProviderRecord按照类名和authority分别存储到mProviderMap当中,ContentProviderHolder会存储到ContentProviderRecord当中。

最后也放一下整个流程的流程图方便看代码:

sequenceDiagram
autonumber
box LIGHTYELLOW SYSTEM_SERVER进程
participant AMS
participant ContentProviderHelper
participant PMS
participant ProcessProviderRecord
end
box LIGHTGREEN 应用进程
participant ApplicationThread
participant ActivityThread
participant ContentProvider
end
rect rgb(191, 223, 255)
note right of AMS: attachApplicationLocked
AMS->>+ContentProviderHelper: generateApplicationProvidersLocked
ContentProviderHelper->>+PMS: queryContentProviders
PMS-->>-ContentProviderHelper: List<ProviderInfo>
ContentProviderHelper->>ProcessProviderRecord: installProvider
ContentProviderHelper-->>-AMS: List<ProviderInfo>
AMS->>ApplicationThread: bindApplication(binder call)
end
ApplicationThread->>ActivityThread: handleBindApplication
rect rgb(191, 223, 255)
note right of ActivityThread: installContentProviders
ActivityThread->>ActivityThread: installProvider
ActivityThread->>ContentProvider: attachInfo
ActivityThread->>ActivityThread: installProviderAuthoritiesLocked
end
ActivityThread->>AMS: publishContentProviders
AMS->>ContentProviderHelper: publishContentProviders

至此,就执行完了所有的ContentProvider安装的工作。至于使用ContentProvider的场景,我们之后在继续分析。本文以Android13的代码分析,如果读者对照最好也是以同样版本的代码看。以上是本人关于Android代码阅读的一点分享,由于个人可能存在一些误区,难免会有理解错误,或者笔误,如有发现,欢迎指正,也欢迎读者与我交流Android技术。

(文中类图,时序图使用mermaid绘制,如果使用rss无法渲染,请点击原文查看)

看完评论一下吧

Android源码分析:ClientTransaction分析

2024年8月13日 21:59

分析Android Activity的启动过程,发现Android 在Android9.0中引入了ClientTransaction这一系列的对象,来简化system_server与App进程中处理Activity启动相关的任务。这里就来分析一下。

在服务端(system_server进程)主要有上面这些类,我们首先需要关注的就是ClientTransaction类,这个类在使用的时候主要是有以下几个成员:

1
2
3
4
private List<ClientTransactionItem> mActivityCallbacks;
private ActivityLifecycleItem mLifecycleStateRequest;
private IApplicationThread mClient;
private IBinder mActivityToken;

mClient是对应的app的ApplicationThread,他是一个Binder对象,mActivityToken则是Activity的Binder Token,这两个在很多地方都会看到。而mActivityCallbacks为ClientTransactionItem对象,比如说LaunchActivityItemNewIntentItem这些都是它的子类,同一个ClientTransactionItem中是可以有多个的。mLifecycleStateRequestActivityLifecycleItem,它的子类是PauseActivityItemStopActivityItem这些,每一个是希望Activity执行到的一个状态。相关的类的类图如下:

classDiagram
class Parcelable
<<interface>> Parcelable
class BaseClientRequest
<<abstract>> BaseClientRequest
class ClientTransactionItem
<<abstract>> ClientTransactionItem
BaseClientRequest <|-- ClientTransactionItem
Parcelable <.. ClientTransactionItem
BaseClientRequest: void preExecute(ClientTransactionHandler client, IBinder token)
BaseClientRequest: void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions)
BaseClientRequest: void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions)
ClientTransactionItem: public int getPostExecutionState()
ClientTransactionItem: boolean shouldHaveDefinedPreExecutionState()
class ActivityTransactionItem
<<abstract>> ActivityTransactionItem
ClientTransactionItem <|-- ActivityTransactionItem
ActivityTransactionItem: ActivityClientRecord getActivityClientRecord(ClientTransactionHandler client, IBinder token)
ActivityTransactionItem <|-- LaunchActivityItem
ActivityTransactionItem <|-- NewIntentItem
ActivityTransactionItem <|-- ActivityResultItem
ActivityTransactionItem <|-- `..`
class ActivityLifecycleItem
ActivityTransactionItem <|-- ActivityLifecycleItem
ActivityLifecycleItem: public abstract int getTargetState()
ActivityLifecycleItem <|-- ResumeActivityItem
ActivityLifecycleItem <|-- PauseActivityItem
ActivityLifecycleItem <|-- StartActivityItem
ActivityLifecycleItem <|-- StopActivityItem
ActivityLifecycleItem <|-- DestroyActivityItem

启动Activity时候的代码调用在ActivityTaskSupervisor这个类的realStartActivityLocked方法中,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
final ClientTransaction clientTransaction = ClientTransaction.obtain(
 proc.getThread(), r.token);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
 System.identityHashCode(r), r.info,
 // TODO: Have this take the merged configuration instead of separate global 
 // and override configs. 
 mergedConfiguration.getGlobalConfiguration(),
 mergedConfiguration.getOverrideConfiguration(), r.compat,
 r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
 proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
 results, newIntents, r.takeOptions(), isTransitionForward,
 proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
 r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
 lifecycleItem = ResumeActivityItem.obtain(isTransitionForward,
 r.shouldSendCompatFakeFocus());
} else {
 lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
mService.getLifecycleManager().scheduleTransaction(clientTransaction);

从上面的代码我们看到ClientTransaction以及其他的一些使用到的比较多的对象,Android系统中都做了对象池,内部基本上都是数组维护,我们这里不分析了。

启动Activity就是创建了一个LaunchActivityItem并且设置了对应的LifecycleStateRequest,最后是通过LifecycleManager调用scheduleTransaction来执行。这里的mServiceActivityTaskManagerService的实例,lifecycleManagerClientLifecycleManager的实例,方法代码如下:

1
2
3
4
5
6
7
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
 final IApplicationThread client = transaction.getClient();
 transaction.schedule();
 if (!(client instanceof Binder)) {
 transaction.recycle();
 }
}

这里ClientTransaction会获取Client,也就是IApplicationThread,system_server这一端是ApplicationThread的客户端,因此schedule和recycle方法都会执行。主要看一下schedule方法:

1
2
3
public void schedule() throws RemoteException {
 mClient.scheduleTransaction(this);
}

又调用了Client的scheduleTransaction方法,参数为我们的ClientTransaction方法,这是在客户端调用,那我们需要去服务端看这个方法的执行,实现也就是在ApplicationThread类当中。

1
2
3
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
 ActivityThread.this.scheduleTransaction(transaction);
}

ActivityThreadClientTransactionHandler的子类,这个scheduleTransaction就在ClientTransaction当中,代码如下:

1
2
3
4
5

void scheduleTransaction(ClientTransaction transaction) {
 transaction.preExecute(this);
 sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

其中的preExecute方法就是直接调用了,内部实现就是分别调用所有的activityCallbackpreExecute方法,以及mLifecycleStateRequestpreExecute方法,而execute没有直接调用,而是通过消息发出去了,实现就是ActivityThread的sendMessage,我们也都知道是用它的H这个Handler,可以在它的handleMessage中找到如下代码:

1
2
3
4
5
6
case EXECUTE_TRANSACTION:
 final ClientTransaction transaction = (ClientTransaction) msg.obj;
 mTransactionExecutor.execute(transaction);
 if (isSystem()) {
 transaction.recycle();
 }

可以看到这里是通过TransactionExecutor来调用execute,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public void execute(ClientTransaction transaction) {
 final IBinder token = transaction.getActivityToken();
 if (token != null) {
 final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
 mTransactionHandler.getActivitiesToBeDestroyed();
 final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
 if (destroyItem != null) {
 if (transaction.getLifecycleStateRequest() == destroyItem) {
 activitiesToBeDestroyed.remove(token);
 }
 if (mTransactionHandler.getActivityClient(token) == null) {
 return;
 }
 }
 }

 executeCallbacks(transaction);
 executeLifecycleState(transaction);
 mPendingActions.clear();
}

public void executeCallbacks(ClientTransaction transaction) {
 final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
 if (callbacks == null || callbacks.isEmpty()) {
 return;
 }

 final IBinder token = transaction.getActivityToken();
 ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

 final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
 final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
 : UNDEFINED;
 final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

 final int size = callbacks.size();
 for (int i = 0; i < size; ++i) {
 final ClientTransactionItem item = callbacks.get(i);
 final int postExecutionState = item.getPostExecutionState();

 if (item.shouldHaveDefinedPreExecutionState()) {
 final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
 item.getPostExecutionState());
 if (closestPreExecutionState != UNDEFINED) {
 cycleToPath(r, closestPreExecutionState, transaction);
 }
 }

 item.execute(mTransactionHandler, token, mPendingActions);
 item.postExecute(mTransactionHandler, token, mPendingActions);
 if (r == null) {
 r = mTransactionHandler.getActivityClient(token);
 }

 if (postExecutionState != UNDEFINED && r != null) {
 // Skip the very last transition and perform it by explicit state request instead. 
 final boolean shouldExcludeLastTransition =
 i == lastCallbackRequestingState && finalState == postExecutionState;
 cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
 }
 }
}

execute中调用了executeCallbacks和executeLifecycleState这两个方法,我们上面贴出了前一个方法的代码,这里先来分析。 首先来介绍一下TransactionExecutor的一个成员变量mTransactionHandler我们可以发现TransactionExecutor的初始化是在ActivityThread中,这个的mTransactionHandler就是ActivityThread。 这里首先会先判断Activity是否需要执行到某一个状态,也就是通过getPostExecutionState来设置,比如NewItentItem中是有设置的,而LaunchActivityItem则不需要。如果Activity需要进入指定的状态,则会调用cycleToPath来执行到对应的状态,我们后面再分析。 随后就会调用item的execute方法和postExecute方法。我们就来看一下LaunchActivityItem的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void execute(ClientTransactionHandler client, IBinder token,
 PendingTransactionActions pendingActions) {
 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
 ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
 mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
 mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
 client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
 mTaskFragmentToken);
 client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

我们知道这里的client就是我们的ActivityThread,而这个handleLaunchActivity的实现也是在ActivityThread中实现,而启动Activity的参数都是在这个LaunchActivityItem里面的,他们通过Binder跨进程从system_server传到了app进程。这样一来原来放在ApplicationThread当中的handleLaunchActivity方法就抽离到LaunchActivity中了,ActivityThread这个文件中的很多代码就抽出去了。Activity启动的流程这里就不分析了,继续去看看后面的代码。

这里执行完了还是会检查是否需要把Activity推进到某一个状态,如果是LaunchActivityItem还是不需要的。我们继续去看executeLifecycleState的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void executeLifecycleState(ClientTransaction transaction) {
 final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
 if (lifecycleItem == null) {
 // No lifecycle request, return early. 
 return;
 }

 final IBinder token = transaction.getActivityToken();
 final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

 if (r == null) {
 // Ignore requests for non-existent client records for now. 
 return;
 }

 // Cycle to the state right before the final requested state. 
 cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);

 // Execute the final transition with proper parameters. 
 lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
 lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

可以看到其中还是首先调用了cycleToPath这个方法,但是我们需要注意这里这个方法的调用,excludeLastState这个值传的是true,也就是说如果我们设置的LifecycleStateResumeActivityItem那么它会把状态设置为ON_START而不是ON_RESUME。为什么这样做呢,因为后面还会调用lifecycleItem.execute,在其中我们会自行把状态推进到我们需要的状态,ResumeActivityItem的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public void execute(ClientTransactionHandler client, ActivityClientRecord r,
 PendingTransactionActions pendingActions) {
 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
 client.handleResumeActivity(r, true /* finalStateRequest */, mIsForward,
 mShouldSendCompatFakeFocus, "RESUME_ACTIVITY");
 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
 PendingTransactionActions pendingActions) {
 ActivityClient.getInstance().activityResumed(token, client.isHandleSplashScreenExit(token));
}

上面的代码中可以看到execute中是调用的ActivityThreadhandleResumeActivity方法,从而让Activity执行resume并且进入ON_RESUME状态。 我们再来看一下cycleToPath方法:

1
2
3
4
5
6
private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
 ClientTransaction transaction) {
 final int start = r.getLifecycleState();
 final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
 performLifecycleSequence(r, path, transaction);
}

其中首先是拿到Activity当前的状态,再通过mHelper拿到我们需要执行的所有状态,代码如下(异常判断代码移除了,这里关注重点):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {


 mLifecycleSequence.clear();
 if (finish >= start) {
 if (start == ON_START && finish == ON_STOP) {
 mLifecycleSequence.add(ON_STOP);
 } else {
 // just go there 
 for (int i = start + 1; i <= finish; i++) {
 mLifecycleSequence.add(i);
 }
 }
 } else { // finish < start, can't just cycle down 
 if (start == ON_PAUSE && finish == ON_RESUME) {
 // Special case when we can just directly go to resumed state. 
 mLifecycleSequence.add(ON_RESUME);
 } else if (start <= ON_STOP && finish >= ON_START) {
 // Restart and go to required state. 

 // Go to stopped state first. for (int i = start + 1; i <= ON_STOP; i++) { 
 mLifecycleSequence.add(i);
 }
 // Restart 
 mLifecycleSequence.add(ON_RESTART);
 // Go to required state 
 for (int i = ON_START; i <= finish; i++) {
 mLifecycleSequence.add(i);
 }
 } else {
 // Relaunch and go to required state 

 // Go to destroyed state first. for (int i = start + 1; i <= ON_DESTROY; i++) { 
 mLifecycleSequence.add(i);
 }
 // Go to required state 
 for (int i = ON_CREATE; i <= finish; i++) {
 mLifecycleSequence.add(i);
 }
 }
 }

 // Remove last transition in case we want to perform it with some specific params. 
 if (excludeLastState && mLifecycleSequence.size() != 0) {
 mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
 }

 return mLifecycleSequence;
}

可以看到以上代码还是比较简单的,就是按照顺序把Android Activity生命周期状态,按照当前的状态和需要执行结束的状态,把需要执行的放到数组中,最后再看看最后的那个状态要不要移除掉。我们继续看performLifecycleSequence的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
 ClientTransaction transaction) {
 final int size = path.size();
 for (int i = 0, state; i < size; i++) {
 state = path.get(i);
 switch (state) {
 case ON_CREATE:
 mTransactionHandler.handleLaunchActivity(r, mPendingActions,
 null /* customIntent */);
 break;
 case ON_START:
 mTransactionHandler.handleStartActivity(r, mPendingActions,
 null /* activityOptions */);
 break;
 case ON_RESUME:
 mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
 r.isForward, false /* shouldSendCompatFakeFocus */,
 "LIFECYCLER_RESUME_ACTIVITY");
 break;
 case ON_PAUSE:
 mTransactionHandler.handlePauseActivity(r, false /* finished */,
 false /* userLeaving */, 0 /* configChanges */,
 false /* autoEnteringPip */, mPendingActions,
 "LIFECYCLER_PAUSE_ACTIVITY");
 break;
 case ON_STOP:
 mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
 mPendingActions, false /* finalStateRequest */,
 "LIFECYCLER_STOP_ACTIVITY");
 break;
 case ON_DESTROY:
 mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
 0 /* configChanges */, false /* getNonConfigInstance */,
 "performLifecycleSequence. cycling to:" + path.get(size - 1));
 break;
 case ON_RESTART:
 mTransactionHandler.performRestartActivity(r, false /* start */);
 break;
 default:
 throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
 }
 }
}

这里可以看到代码其实很简单,就是按照顺序调用mTransactionHandler也就是ActivityThread的各个生命周期需要执行的方法。

ActivityThread和TransactionExecutor的关系如下图:

classDiagram
class ActivityThread
class TransactionExecutor
class ClientTransactionHandler
<<abstract>> ClientTransactionHandler
ClientTransactionHandler <|-- ActivityThread
TransactionExecutor .. ClientTransactionHandler
ActivityThread *-- TransactionExecutor
ActivityThread: TransactionExecutor mTransactionExecutor
TransactionExecutor: ClientTransactionHandler mTransactionHandler
TransactionExecutor: execute(ClientTransaction transaction)

这里也画一下时序图方便看代码,发送端的代码比较简单,这里只画一下接收和执行端的图

sequenceDiagram
autonumber
ApplicationThread->>ActivityThread: scheduleTransaction
ActivityThread->>ClientTransaction: preExecute
ActivityThread->>ActivityThread: sendMessage:EXECUTE_TRANSACTION
ActivityThread->>TransactionExecutor: execute
TransactionExecutor->>TransactionExecutor: executeCallbacks
TransactionExecutor->>ClientTransaction: execute
TransactionExecutor->>ClientTransaction:postExecute
TransactionExecutor->>TransactionExecutor: executeLifecycleState
TransactionExecutor->>TransactionExecutor: cycleToPath
TransactionExecutor->>ActivityThread: handleToTargetStatus

上面的分析是system_server调用,当然App进程也是可以自己调用的,比如下面的代码:

1
2
3
4
5
6
private void scheduleResume(ActivityClientRecord r) {
 final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
 transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(/* isForward */ false,
 /* shouldSendCompatFakeFocus */ false));
 executeTransaction(transaction);
}

可以看到,在app进程中就直接调用TransactionExecutor的execut去执行了。 以上就是ClientTransaction的全部分析了。我也是在分析Android Activity启动的过程发现这些代码的,Activity的启动代码量巨大,在Android 9的时候把一部分代码抽离到ClientTransaction中去,在Android 10以后启动的相当一部分代码抽离到ActivityTaskManagerService中去了,让代码逻辑更清晰一点,不过绕来绕去的看代码也是很麻烦。

因此,这里先试试水,把其中的一小部分也就是ClientTransaction相关的先拿出来写一写。从我的角度看,为什么要抽出来ClientTransaction这个机制,原先所有的代码都需要在ApplicationThread中定义binder接口,而android随着各种屏幕的出现,小窗模式等等,这样的化每次有新功能都需要增加新的binder方法,而用了ClientTransaction则都可以通过scheduleTransaction来调用,同时ActivityThread内的代码也会有所减少,代码功能更加独立。

Android系统因为功能的代码,代码也更加复杂,很多地方因为许多的新功能多了很多逻辑判断,为我们看代码增加了难度。但是只要我们记住我们看的主线,关注我们一路传过来的值,关注我们自己会执行的那个分支,这样一路下来就可以把整个逻辑理清楚。

本文以Android13的代码分析,如果读者对照最好也是以同样版本的代码看。以上是本人关于Android代码阅读的一点分享,由于个人可能存在一些误区,难免会有理解错误,或者笔误,如有发现,欢迎指正,也欢迎读者与我交流Android技术。

(文中类图,时序图使用mermaid绘制,如果使用rss无法渲染,请点击原文查看)

看完评论一下吧

记录再次编译Android系统的坑

2024年7月29日 11:05

之前已经多次编译过Android系统的代码,但是一直没有静下来去阅读Android源代码。最近不太忙,决定开始好好读读系统源码。这篇文章作为开篇,先记录把Android系统编译出来,并且把源码阅读的环境准备好。

首先介绍一下,这次使用的是Ubuntu22.0.4 LTS版本,起初准备使用虚拟机安装,但是映射虚拟硬盘老是出问题,还是直接搞上了Windows & Ubuntu双系统,Ubuntu安装到移动硬盘里面,插上移动硬盘就能用Ubuntu,拔掉还能使用Windows系统。为什么要用移动硬盘,因为官方说了至少要留400G空间给源码和编译所需,不过我最后测下来300G也是够的。

为什么选择Ubuntu 22.0.4而不是最新的Ubuntu 24.0.4,是因为22.0.4后面的版本移除了libncurses.so.5,在22.0.4版本的时候我们还可以通过以下的命令安装,而后面的版本我们可能就只能使用android源码提供的相关库,并且自己去做文件的映射处理,反正我试过之后发现还是有问题,就重新安装了Ubuntu 22.0.4。

1
sudo apt install libncurses5

除此之外我们按照官方教程来做就行了。

首先是安装必备的软件工具包:

1
sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig

安装repo,可以自己下载最新版本,也只直接使用ubuntu的软件包安装:

1
sudo apt-get install repo

设置git的用户信息:

1
2
git config --global user.name "user name"
git config --global user.email "user@email.com"

创建一个目录,然后在这个目录下面初始化Android系统 Repo

1
repo init -u https://android.googlesource.com/platform/manifest -b master

-u后面是Android仓库的清单文件地址, -b是我们要拉出来的代码分支,可以是分支名称,也可以是tag名称,我选择的是Android 13的源码,用了这个tag: android-13.0.0_r83

然后就可以调用repo sync来下载代码了,这个过程可能需要等待几个小时,看你的网速,可以在后面加上 -c -j线程数来加快速度,-c表示拉当前分支,-j开启多个线程下载,如:

1
repo sync -c -j16

下载过程中如果中断了,重新执行这个命令可以继续下载,如果有遇到错误说有.lock文件,去子文件夹的.git文件夹下面找到相关的lock文件删除再重试就行了。

下载完我们在工作目录,首先执行以下代码,初始化工作环境和一些命令:

1
source ./build/envsetup.sh

执行以下命令,初始化我们要构建的目标:

1
lunch sdk_phone_x86_64-eng

以上这两句,我们需要每次启动一个终端或者重启电脑后都需要运行,不然m和emulator等命令都用不了。

然后后面的目标也可以不写,这样会进入一个选择列表让你选要构建的目标。 之后就可以输入m来构建系统了。

构建完系统在命令行执行emulator理论上就可以在模拟器中运行我们的系统了。但是我这里模拟器确黑屏了,只有一个小窗口,运行失败,命令行日志只看到libvulkan.so移动失败,但是看了以下模拟器的目录下面,是有这个文件的,然后在这台电脑上安装了android sdk,在其中创建了AVD,并启动它,发现是可以的。 这个时候想到一个妙招,就是把sdk的模拟器拷贝到我们源码的模拟器目录,把这边的模拟器给替换掉。然后神奇的事情发生了,我们编译出来的系统运行成功了。如果你遇到类似的情况也可以这样试试,把android sdk目录中的模拟器复制到./prebuilts/android-emulator/linux-x86_64目录下面。

关于阅读源码,之前大家都是使用idegen来生成适用于Android Studio的工程文件,但是默认会把所有文件都导入,打开速度极慢,我想这可能也是我之前无法把代码阅读下去的一个理由。在去年,android官方推出了Android Studio For Platform,可以这里下载: https://developer.android.com/studio/platform ,UI跟Android Stuido一样,不过它可以帮我们自动执行source 和launch命令,以及对于platform的module的设置,导入的时候我们选择自己要构建的target就行了,使用几天下来是很好用的。

除了使用Android Studio platform 在Android 10之后google还为我们提供了AIDEGen这个工具,我就没花时间去用了,感兴趣的可以看看这个博主的文章:https://juejin.cn/post/7276812358663733263

以上就是我这次的编译过程,期待对大家有用。在此立个Flag,后续把阅读Android源码的内容也写出来。

一些参考的资料:

  1. 下载 Android 源代码-官方资料
  2. 针对 AOSP 开发进行设置(9.0 或更高版本)-官方资料
  3. 构建Android-官方资料
  4. AndroidStudio导入Android系统源码

看完评论一下吧

一个Android开发者的Google IO 2024信息汇总

2024年5月21日 21:53

AI和大模型很火,今年的google io上面感觉各个方向都和AI有关,Android平台相关的东西倒是感觉不太多了。我这里整理一下Android相关的信息。Android主要就是Android 15的发布,以及jetpack compose的更新和google play的一些更新。

AI与Android

Android 14开始手机会内置Gemini Nano,开发者可以通过AI Core来调用AI的能力,目前是google pixel 8 pro和三星的s24已经内置了。除了端上,开发这还可以使用Firebase新提供的一些能力使用AI模型。

对于开发者来说,除了开发应用提供AI能力给用户,开发过程中也能体验到google提供的AI能力,下载Android Studio Koala即可使用,提供了类似github copilot的功能,有代码补全,自动代码生成,对话生成代码,UI设计图生成代码,代码重构等功能。

Android 15

每年一个大版本现在是Android系统的惯例了。google io开始的这天发布了Android 15 Beta2。

摄像头方面引入了低光增强功能,这个对于国产手机拍照功能基本都是已有功能,不过对于开发者可以通过Camera2的接口来使用相机提供的这个功能。同时还提供了新的闪光等强度控制API,在支持的设备上更好的控制相机硬件和算法。

文字方面,中日韩语的字体,android上的是NotoSansCJK,新的版本将是可变的了,这代表可以中文可以有更多的样式和变化了。Android 15开始,可使用 JUSTIFICATION_MODE_INTER_CHARACTER 利用字母间距将文本两端对齐,Android 8.0引入了字词间对其,这对于中文这种单个字符的语言是不友好的,新的字符间对其可以大大改善中文排版的美观度。Android 15中还提供了标记来控制换行,即 <nobreak>来避免换行,<nohyphen>避免断字。

1
2
3
<resources>
 <string name="pixel8pro">The power and brains behind <nobreak>Pixel 8 Pro.</nobreak></string>
</resources>

未使用nobreak 使用nobreak

对于GPU的使用,Android在7.0的时候引入了Vulkan,在今年他们有引入了ANGLE,这个对于普通开发者不需要关注,对于游戏开发者或者需要使用Open GL进行图形处理的需要注意,毕竟Google计划未来只支持通过ANGLE使用Open GL。

预测性返回动画之前在android14中就有了,但是需要在开发者模式中手动打开,普通用户还用不了,现在默认打开了。

最小target sdk version提高到24,低于此版本的将无法在Android15的手机上安装。

另外关于前台服务,隐私空间,系统UI等方面也有一些提升和新功能。更多内容可以看Android 15的文档: https://developer.android.com/about/versions/15/summary

Kotlin和Jetpack compose

Kotlin 主要介绍了jetpack的更多库适配了kmp,让开发者可以在更多平台共用代码。具体可查看文档:https://android-developers.googleblog.com/2024/05/android-support-for-kotlin-multiplatform-to-share-business-logic-across-mobile-web-server-desktop.html

Jetpack compose支持了share elements 动画,列表动画,Text支持链接和html富文本而不必使用ClickableText,以及一些性能提升。当然,最大的变化应该属于在kotlin 2.0的时候,compose的编译器将从谷歌的代码库移到kotlin的代码库,对于我们来说这意味着compose的编译器插件版本将和kotlin一样,减少我们升级版本的很多烦恼。更多详见:https://android-developers.googleblog.com/2024/05/whats-new-in-jetpack-compose-at-io-24.html

同时Compse对于更多尺寸的屏幕,以及手表,电视等等有了更好的支持。compose adaptive 库提供了一些api来让我们更多的适配各种屏幕尺寸,主要的是NavigationSuiteScaffold, ListDetailPaneScaffold, SupportingPaneScaffold。这次大会更新了Compose for wearos 的库,让更多功能稳定下来。正式发布了Compose for TV的1.0。这样下来所有的Android 平台都可以使用Compose进行开发了。

我们的桌面小组件,也发布了Jetpack Glance 1.1,来让我们支持使用Compose来编写桌面小组件。当然其中的一些widget和普通compose拥有一样的名称,但是却来自不同的package,因为最后还是会编译成remoteview,因此不能混用。

由此可见Android的原生开发以后将是Compose的天下,加油学吧。

Flutter

Dart支持了宏,从而对于Json的序列化和反序列化会更加简单。具体的使用方法: https://dart.dev/go/macros

Flutter 通过WebAssembly在浏览器上面运行性能更好,因此官方后面在Web的方向应该是WebAssembly了, 对于Flutter转js这个方案应该会被放弃。

Google Play

谷歌正式放出了隐私合规检查工具 Checks,可以帮助我们检查app的隐私合规的问题,检查app收集的用户数据,使用的权限,sdk等等,在当前各个国家对于隐私政策越来越严的当下,这个东西挺不错的。访问官网了解:https://checks.google.com/

谷歌在2021年发布了Google Play SDK Console给一些大的SDK开发者,现在这个Consle开放给了所有SDK开发者,借助这个平台SDK开发者可以在sdk有漏洞或者安全问题时,让使用到sdk的用户在谷歌得到通知来升级sdk。同时还可以查看sdk的用户数据,以及让应用开发者共享sdk的崩溃和卡顿日志给sdk的开发者,从而更快的解决问题。

谷歌还发布了Engage SDK, 帮助开发者在google play 内展示app的内容,吸引用户使用我们的应用,但是这个SDK需要开发者在媒体体验计划中或者应用用至少100K的DAU才能申请。当然除了这个sdk,新的google play还支持我们使用搜索关键定制商店详情页,这样可以对不同的来源做定制,提高用户下载app的转化率。应用详情页之前只能展示针对当前设备的屏幕截图和介绍,新版本将支持展示所有支持的设备的信息。

Play Integrity API也有更新,新增加了应用访问风险检查,服务端接口也可以使用play protect的验证。

新版本的Google play后台中还支持对于Deeplink 的管理,通过Google play甚至可以变更deeplink, 而不用更新app。

更多的内容还是请参考官方内容:

Android系统的更新:https://developer.android.com/about/versions/15/summary

Android Studio 的更新: https://android-developers.googleblog.com/2024/05/google-io-2024-whats-new-in-android-development-tools.html

Google Play的更新: https://android-developers.googleblog.com/2024/05/io-24-whats-new-in-google-play.html

Compose的更新: https://android-developers.googleblog.com/2024/05/whats-new-in-jetpack-compose-at-io-24.html

Flutter 更新: https://docs.flutter.dev/release/whats-new

看完评论一下吧

❌