热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Androidactivityexported属性理解

背景这么久了,我自己看来对此属性的理解有点小偏差,当然不是表面上的理解误差,而是涉及到具体实现的细节。这里先贴下官方关于此属性的解释:android:exportedThisele

背景

这么久了,我自己看来对此属性的理解有点小偏差,当然不是表面上的理解误差,而是涉及到具体实现的细节。这里先贴下官方关于此属性的解释:

android:exported
This element sets whether the activity can be launched by components of other applications — “true” if it can be, and “false” if not. If “false”, the activity can be launched only by components of the same application or applications with the same user ID.
If you are using intent filters, you should not set this element “false”. If you do so, and an app tries to call the activity, system throws an ActivityNotFoundException. Instead, you should prevent other apps from calling the activity by not setting intent filters for it.

If you do not have intent filters, the default value for this element is “false”. If you set the element “true”, the activity is accessible to any app that knows its exact class name, but does not resolve when the system tries to match an implicit intent.

This attribute is not the only way to limit an activity’s exposure to other applications. You can also use a permission to limit the external entities that can invoke the activity (see the permission attribute).

这段文字说明,值得多读几遍!!!

由于我们团队的关系,我们开发的模块经常需要集成到多个app中,而我们不想为某个app单独维护一份代码,即我们的开发中,所有的宿主app用的都是同一套代码。比如就存在类似这样的代码:

android:name=".SubActivity"
android:cOnfigChanges="orientation|keyboardHidden"
android:exported="false" // 注意这行代码!!!
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden">







这样在宿主app里,通过打开mlpf://sub这样的短链就能轻松地来到我们模块的SubActivity。注意这里android:exported=false的设置,因为如果不设置的话,根据Android的规则只要有intent-filter存在,那么exported就是true,即对外暴露的;而这里我们显然不希望是对外暴露的,因为如果这样的话,当安装了多个集成了我们模块的App时,当要打开这样的短链请求时系统就会弹出选择框让用户选择在哪个app里打开,这当然不是我们期望的。

当同一个设备上装了我们的多个app的时候,在8.0之前都是ok的,即A app里的SubActivity和B app里的SubActivity互相是没任何关系的,也是互相看不到对方的,这是我们对exported=false的认识;直到上周某天晚上快要下班了,QA同学拿着升级到8.0的Nexus 6P跟我说,你看你们这个页面跳不过去了,还弹出了个讨厌的没有应用可执行此操作的提示,我当时也是一脸懵逼啊,但心里已经有种不祥的预感,看起来像是google改出来的bug。

我接过设备,点击了几下,确保能复现,然后连着电脑,看了下adb logcat关于ActivityManager相关的输出,果然我们这个intent没有找到对应的cmp(component),而是到了系统的ResolverActivity,ResolverAct大家都知道,当系统找到了多个目标或者没目标时会弹出它提醒用户。这就有点奇怪了,同样的case在7.x的设备上就是好的,虽然行为上也是到了ResolverAct,但ResolverAct内部最终还是导到了本app内部的SubActivity,最终正确调起了。

解惑

有一点我们需要知道,即当我们通过Intent打开act的时候,系统内部会调用
Intent.resolveActivity(pm),其内部又会接着调用PackageManager#resolveActivity。另外你也可以调用PackageManager#queryIntentActivities来查看某个intent究竟可以被哪个act处理。有一点需要特别注意的是,这些方法会考察设备上所有安装的app里的activity,即使是那些被显式标记成了exported=false的act,这就是我上文说到的理解偏差,这让我很惊讶。因为我以前的认识中,既然标记了不对外暴露,那么这些act也不应该被找到才对,但很可惜,看起来Android的实现不是这样的,关于这点,可以参考以下问题:Android queryintentactivities.

7.x(包括)之前虽然也能查到别的app里exported=false的act(这个行为看起来一直都有),但最终会正确打开匹配到的本app里exported=false的act,但在8.0上这个行为break掉了,直接变成了上文提到的“没有应用可执行此操作”,真是一个忧伤的故事。

8.0解决办法

关于8.0的这个问题,AOSP上也有人报了bug:intent有多个match时无法正确跳转。不过看起来仅仅是个没多少关注的P3bug,而且我手头的5x升级到了8.1.0,此问题依然存在,看来指望google修复希望不大。还好我们也有办法处理下,看下Intent.setPackage方法,如下:

/**
* (Usually optional) Set an explicit application package name that limits
* the components this Intent will resolve to. If left to the default
* value of null, all components in all applications will considered.
* If non-null, the Intent can only match the components in the given
* application package.
*
* @param packageName The name of the application package to handle the
* intent, or null to allow any application package.
*
* @return Returns the same Intent object, for chaining multiple calls
* into a single statement.
*
* @see #getPackage
* @see #resolveActivity
*/
public Intent setPackage(String packageName) {
if (packageName != null && mSelector != null) {
throw new IllegalArgumentException(
"Can't set package name when selector is already set");
}
mPackage = packageName;
return this;
}

我们面临的主要问题就是系统API在startActivity的过程中查到了别的app里面的非暴露act,这个方法看起来刚好可以将系统的这个查找行为局限在本app内,所以我们的fix如下:

if (Build.VERSION.SDK_INT >= 26) {
intent.setPackage(mContext.getPackageName());
}

最后,关于exported=false的实现,我个人的看法是应该再提早些,直接一开始在匹配的过程中就找不到这样的act,而不是一股脑全找到(导致本来就1个target满足,结果找了多个出来),等到最后要打开了,看下exported是false,才弹个无权限的错误!!!之前魅族更新了次系统后也出过这问题,弹出让用户选,结果选了之后又告诉用户无权限(因为实际是exported=false的activity)。就像在实现某个方法的时候,有些前置条件不满足,我们应该尽早return,而不是埋头做了很多工作后,才检查一些必要条件,发现不对了才退出,fail fast常常是很好用的策略。

ps:实在是没明白google这里的实现为啥要找到这些实际上private的act,看起来完全是在做无用功啊,反正怎么着都不可能打开,你把它找出来干啥呢!!!有想法的同学可以留言交流下,谢谢。


推荐阅读
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 先看看ElementUI里关于el-table的template数据结构:<template><el-table:datatableData><e ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • Explain如何助力SQL语句的优化及其分析方法
    本文介绍了Explain如何助力SQL语句的优化以及分析方法。Explain是一个数据库SQL语句的模拟器,通过对SQL语句的模拟返回一个性能分析表,从而帮助工程师了解程序运行缓慢的原因。文章还介绍了Explain运行方法以及如何分析Explain表格中各个字段的含义。MySQL 5.5开始支持Explain功能,但仅限于select语句,而MySQL 5.7逐渐支持对update、delete和insert语句的模拟和分析。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
author-avatar
mobiledu2502938445
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有