一、前言
如果你的 App 内,有使用到隐式 Intent 去与其他 App 交互。例如,分享一个链接,当你的系统升级到 Android M(Api level 23) 之后,你就需要小心了,它可能并不能按照你的意愿去执行你想执行的任务。
举个例子,你想分享一个链接,而每次都想让用户主动去选择一个,但是有时候这个操作并不能如愿,如果用户主动指定了某个 App 打开这个 链接,你的 App 将直接使用用户指定的 App 打开这个链接(其实是有例外情况的,后面说)。
二、Android M 改变了什么
一般我们想要分享一个链接出去,直接使用 隐式 Intent 即可。
在 Android M 之下,它会调起系统内,所有符合的 App 供用户选择一个合适的去打开。
可以看到,默认会弹出一个选择器,让用户选择一个 App 来处理。这没什么好说的,是一个常规的操作,应该几乎所有的 Android 开发人员,都是不陌生的。
除了这个系统的选择器,我们也是可以在代码中,去查询到符合 Intent 过滤要求的 App 的。需要使用 PackagemManager.queryIntentActivitys() 方法来进行查询,上面的 demo 中,也通过 Log 打印出了它的输出。
看输出,和我们选择器中的是一致的。
但是,这一切在 Android M 就开始改变了。
Android M 推出了 App Links 是的概念,系统将通过你的配置的网址进行身份验证,如果通过验证,你需要打开的链接,将直接使用你的 App 来打开,而不是直接跳转到一个浏览器,这个过程是不会向用户询问的。但是 App Links 依托于 Google 的服务,所以国内开发者应该享受不到它的便利。
但是处理通过网站的服务器来进行身份验证之外,还可以在设置页面中,对默认的程序进行设定。
路径是:系统设置 → 应用 → 应用程序 → 默认打开
例如这里打开了 Google + 这款 App 的默认打开页面。
可以在『打开支持的链接』中,选择是否默认打开使用它来完成打开链接的操作。
一旦选择了『在此应用中打开』,在去运行上面的程序,你将直接使用 Google+ 打开你的链接,而用户将失去选择的权利,除非用户将这里重新设置为『每次都询问』。
三、例外情况
实际上,这也是存在例外的情况的。
3.1 过滤会先校验 domain
下面是一个标准的 URL 格式:
scheme://host.domain:port/path
这种隐式 Intent ,优先考虑能与 domain 匹配的 App,这是系统内部的一个优先级策略。
例如,现在在我的系统中,有一款 HiitTime 的 App,它设定了一个 m.hiittime.com 的链接支持。
再修改前面提供的 Demo,只是把链接指向了它。
这样的情况下,哪怕我们也将 Google+ 设置为『在此应用内打开』,它最终也是允许用户去选择 HttiTime 这个 App 来处理的。
3.2 系统 App 优先级高
前面 HiitTime 的例子可以看到,实际上我是没有指定 Chrome 来处理的,但是选择器中,实际上是会有 Chrome 的选择的,这个我暂时没有发现可以去掉它的办法。
而在 Chrome 的默认开打的选择页面中,也是无法操作的,我只能理解为它对系统 App 做了特殊的保护处理。
四、辟谣
之前看到有人说可以在对 Intent 的 Uri 只设定 scheme ,例如,’http://‘ 然后后面什么都不跟,这样系统就会列出所有的浏览器供你选择,然后我们就可以在选择列表中,手动指定打开的 App ,这样就可以很好的打开它了。
但是实测,这样是行不通的,当设置了 Google+ 『在此应用内打开』的情况下,依然会直接使用 Google+ 来完成我们的操作,所以这个操作是不成立的。
但是也提供了另外的一个思路,我们实际上是可以通过 PackageManager 拿到系统中安装的 App 的,而通常我们需要分享打开的 App 就那么多,所以我们可以以穷举的方式,筛选出需要的 App ,自己来定义这个选择器,然后指定对 Intent 指定 PackageName 就可以完成对指定 App 的分享,而绕过『在此应用内打开』的设定。
这只是一个思路,没有实际实验,但是应该可行。
五、小结
如果你的 App 内,有这种隐式的操作,就应该检查一下是否在这样的情况下,依然能完成我们如期的功能。
【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】