Android 11 中的软件包可见性适配 -- 线上踩坑记录

4,518 阅读5分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

问题定位

因为做的是海外项目,早上问题反馈群里出现了twitter 点击登录时候提示: 未安装。 在项目本地查了下是提示文案, 是因为调用了

//根据包名 获取应用信息,如果不抛异常就是已经安装app
context.getPackageManager().getApplicationInfo("com.twitter.android",
      PackageManager.MATCH_UNINSTALLED_PACKAGES);

ps:代码是为了方便演示简化后的代码。实际是封装在公共库里面的代码。 看代码没有啥问题,况且三方登录这块儿好久没有修改了 理论不应该出现问题。

  • 怀疑用户网络下载的apk 是被第三方市场或者灰产处理过的,所以包名不是官方的包名称 这个时候让QA去复现一下,好家伙,好几个手机都不可以了。 找他们要到twitter.apk 然后通过命令查看了下apk信息,确实发现包名不是 com.twitter.android ,本来以为事情到这里应该结束了,然后并没有。。。 先用aapt查看拿到的apk信息: 在终端输入:
aapt dump badging Twitter_Rev_ali213.apk

然后输入了Twitter_Rev_ali213.apk 的所有信息

//重点信息: 包名 
package: name='com.fengyintime.rongshu' versionCode='104' versionName='1.0.4' compileSdkVersion='23' compileSdkVersionCodename='6.0-2438415'

install-location:'internalOnly'

sdkVersion:'21'

targetSdkVersion:'30'

uses-permission: name='com.twitter.android.permission.RESTRICTED'

uses-permission: name='android.permission.MODIFY_AUDIO_SETTINGS'

uses-permission: name='android.permission.INTERNET'

...
...
...

uses-permission: name='android.permission.INTERNET'

uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'

application-label:'Twitter'

application-label-ar:'‏تويتر'

application-label-bg:'Twitter'
...
...
locales: '--_--' 'ar' 'bg' 'bn' 'ca' 'cs' 'da' 'de' 'el' 'en-GB' 'es' 'fa' 'fi' 'fr' 'gu' 'hi' 'hr' 'hu' 'in' 'it' 'iw' 'ja' 'kn' 'ko' 'mr' 'ms' 'nb' 'nl' 'pl' 'pt' 'ro' 'ru' 'sk' 'sr' 'sv' 'ta' 'th' 'tl' 'tr' 'uk' 'vi' 'zh-CN' 'zh-HK' 'zh-TW'

densities: '120' '160' '240' '320' '480' '640' '65534' '65535'

native-code: 'arm64-v8a' 'armeabi-v7a' 'x86'
  • 然后在让从google play市场下载apk,发现一个神奇的事情,就是Android 11 系统不可以, 低于11的系统可以正常登录。

这个时候才去插上USB数据线,然后看下是不是有什么日志输出。点击时候直接看到 android.content.pm.PackageManager$NameNotFoundException: com.twitter.android 异常, 这个时候我知道,一定是代码出问题了,或者我们apk的问题,一定不是用户的问题。 然后查了下为啥11系统会报着个错误,接着就发现问题了。

异常配图1.png

然后看了下我们 项目targetSdkVersion 版本修改记录是最近一个版本从29升级到30 ,这块儿被忽视了 没有测试到。

问题解决方案

方案一

找到了官方文档,在Android 11系统,项目targetSdkVersion =30 的时候 对安装app列表做了限制,就是你不能直接向开始那样读取安装列表,或者查询某个app信息。需要单独配置,或者给配置权限,不然读取不到。 直呼好家伙。差点就。。。。。

google官网给我们解决方案了:如果知道要查询或与之交互的一组特定应用(例如,与您的应用集成的应用或您使用其服务的应用),请将其软件包名称添加到 <queries> 元素内的一组 <package> 元素中:

<manifest package="com.example.app">
    <queries>
        <package android:name="com.twitter.android" />
        <package android:name="com.tencent.mm" />
        ...
        ...
    </queries>
    ...
</manifest>

如果我们需要和多个app做交互,直接向<queries>  标签中添加我们要交互的app 包名就可以了。 这个时候你是不是又想说,如果我不知道那个apk包名 但是我想找到对应的apk怎么办呢? 别着急,google粑粑 给我们解决方案了,直接拿过来用。

在给定 intent 过滤器的情况下查询应用及与之交互

您的应用可能需要查询一组具有特定用途的应用或与之交互,但您可能不知道要添加的具体软件包名称。在这种情况下,您可以在 <queries> 元素中列出 intent 过滤器签名。然后,您的应用就可以发现具有匹配的 <intent-filter> 元素的应用。

以下示例允许您的应用看到支持 JPEG 图片共享功能的已安装应用:

<manifest package="com.example.game">
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>
    ...
</manifest>

<intent> 元素有一些限制:

  • 您必须只添加一个 <action> 元素。

  • 您不能在 <data> 元素中使用 pathpathPrefixpathPattern 或 port 属性。系统的行为就像您将每个属性的值都设为通用通配符 (*) 一样。

  • 您不能使用 <data> 元素的 mimeGroup 属性。

  • 在单个 <intent> 元素的 <data> 元素中,您可以使用以下每个属性最多一次:

    • mimeType
    • scheme
    • host

    您可以在多个 <data> 元素之间分配这些属性,也可以在单个 <data> 元素中使用这些属性。

<intent> 元素支持通用通配符 (*) 作为一些属性的值:

  • <action> 元素的 name 属性。
  • <data> 元素的 mimeType 属性的子类型 (image/*)。
  • <data> 元素的 mimeType 属性的类型和子类型 (*/*)。
  • <data> 元素的 scheme 属性。
  • <data> 元素的 host 属性。

除非前面列表中另有说明,否则系统不支持混合使用文本和通配符,如 prefix*

注意:如果您在应用的清单中声明了 <package> 元素,则与该软件包名称关联的应用会出现在对 PackageManager 进行的任何与该应用的组件匹配的查询的结果中。

方案二

直接增加权限.

 <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

这种直接增加权限面临的问题:

  • 但是可能会存在问题, 比如后续权限继续收紧,不及时适配可能出现新的问题。
  • 还有可能会在市场标记我们的应用会读取手机安装app列表的提示,国外用户可能会比较介意。

所以直接增加权限的方案pass了。

总结

以上就是线上问题排查已经处理的全部过程,记录下来方便后边有兄弟遇到同样的问题。

  • 遇到问题一定要刨根问底 不要放过任何细节,因为既然出现了就证明一定有问题
  • 找不到问题直接debug 看代码,总会找到问题的