结合源码谈谈Activity的exported属性 | 游戏极客
Activity的exported属性在单个App可能用得比较少,但对于对外接口的Activity或公司内部多个应用间接口调用的设计会有比较大的影响。本文基于android 6.0.1的源码谈谈Activity的exported属性,内容分为2部分:
系统如何设定exported值
我们知道在AndroidManifest.xml文件中,四大组件都有android:exported属性,是个boolean值,可以为true或false
如:
1 | <activity |
我们这里只讲activity相关,但对其他组件也有参考意义,大家可以去研究。
android:exported是个可选属性,它不像android:name是必须设置的。那么,Android系统是如何设置exported属性的值呢?
通过阅读系统源码,我们知道AndroidManifest.xml是在
frameword\base\core\java\android\content\pm\PackageParse.java
中解析,找到其中解析activity项的源码,在parseActivity函数:
1 | private Activity parseActivity(Package owner, Resources res, |
这里,我们只保留了与exported属性解析相关的源码。从源码中,我们可以得出:
- 如果显式exported属性,不管这个activity有没有设置intent-filter,那么exported的值就是显式设置的值
- 如果没有设置exported属性,那么exported属性取决于这个activity是否有intent-filter
- 如有intent-filter,那么exported的值就是true
- 如没有intent-filter,那么exported的值就是false
如何合理设置exported
了解了系统如何设定exported值之后,我们接下来谈谈在开发中如何更合理设置exported。
我们知道代码离不开用户场景,那么我把一个Activity被调用的场景分为3种:封闭式、半封闭式和开放式
封闭式:被调用的Activity与调用的Context必须在同一个App,其他任何App不能调用
这种是我们最常见的Activity,有2种情况:没有intent-filter情况,可以不设置exported或者设置exported为false
1
2
3
4
5
6
7
8<activity
android:name=".SecondActivity"
android:label="@string/app_name" />
或
<activity
android:name=".SecondActivity"
android:label="@string/app_name"
android:exported="false"/>- 有intent-filter情况,必须设置exported为false
1 | <activity |
- 半封闭式:被调用的Activity只能被部分其他App调用,如同一个公司的2个App之间
这种场景下,除了满足封闭式设置外,还必须把调用App和被调用App设置相同的uid,即在2个App的AndroidManifest.xml添加相同的android:sharedUserId,如
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
为什么添加相同的android:sharedUserId就可以呢?还是看系统AMS启动Activity的源码,在frameword\base\core\java\com\android\server\am\ActivityStackSupervisor.java
1 | final int startActivityLocked(IApplicationThread caller, |
其中就调用mService.checkPermission进行鉴权,mService就是
framework\base\core\java\com\android\server\am\ActivityManagerService.java
checkPermission实际上又调用到
framework\base\core\java\android\app\ActivityManager.java的checkComponentPermission
1 | /** @hide */ |
从代码中我们知道,设置相同的sharedUserId之后,UserHandle.isSameApp(uid, owningUid)就会返回true,因此尽管我们把exported设为false,也不会抛出Permission Denial的异常。
- 开放式:可以被任何App调用
这种场景主要是对外接口,如微信、微博的分享接口。大多数情况下,这样的Activity都会有intent-filter,因此也没必要显式地把exported设为true,不设是可以的,当然不能设为false。但如果你没有intent-filter,那就必须显式地把exported设为true。当然,对于三方app接口的intent-filter设置还有一些要求,如在隐式intent调用必须添加1
android.intent.category.DEFAULT
的category。这个我会在新的文章中介绍。