点击上方“何俊林”,马上关注,每天早上8:50准时推送

真爱,请置顶或星标

转载自公众号:刘望舒,作者: 吃猫猫的鱼

地址:https://juejin.im/post/5cad5b7ce51d456e5a0728b0

1. 导读

Android Q Beta 1刚出,讲道理国内是不到下半年不用理睬Q的,但是上月末的一封华为要求适配Q的邮件要求我们在5月底之前完成相关适配,不然应用会被下架。

一开始还心生奇怪,为什么这次华为的邮件来的那么早以及严格。当我仔细阅读了官方文档之后发现Q的更新特别多,且不适配应用可能无法正常运行(不管targetSDK是否为Q)。

国内相关的文章还比较少,本文将总结归纳AndroidQ官方文档并将自己所踩过的坑记录下来,以便大家少走弯路。

本文将从三个角度介绍Android Q的部分适配问题,也是大家开发适配过程中大概率会遇到的问题:

  • Q 行为变更:所有应用 (不管targetSdk是多少,对所有跑在Q设备上的应用均有影响)

  • Q 行为变更:以 Android Q 为目标平台的应用(targetSDK == Q 才有影响)

  • 项目升级遇到的问题

至于Q的新功能及SDK,我粗略扫了一眼,项目中并没有涉及,故暂不介绍,只放出链接AndroidQ新API及功能。

https://developer.android.com/preview/features

2.Q行为变更:所有应用

用户隐私权限变更

AndroidQ引入了大量更改和限制以增强对用户隐私的保护。

官方文档将这一部分内容独立于Q 行为变更:所有应用来介绍,是因为这一部分内容庞大且重要,个人认为Q的最大更新就是用户隐私权限变更。具体变更的权限如下:

权限

受影响应用

如何启用(影响范围)

存储权限

访问和共享外部存储设备中的文件的应用

adb shell sm set-isolated-storage on(下文详述)

定位权限

在后台时请求访问用户位置信息的应用

这种权限策略在 Android Q 上始终处于启用状态

从后台启动 Activity

不需要用户互动就启动 Activity 的应用

关闭允许系统执行后台活动开发者选项即可启用限制

设备标识符(deviceId)

访问设备序列号或 IMEI 的应用

在搭载 Android Q 的设备上安装应用

无线扫描权限

使用 WLAN API 和 Bluetooth API 的应用

以 Android Q 为目标平台

因为从后台启动Activity权限和无线扫描权限两种权限的变更影响较少。本文不作详述,如有涉及请查阅官方文档。

https://developer.android.com/preview/privacy/background-activity-starts

从后台启动Activity权限变更仅针对与用户毫无交互就启动一个Activity的情况,(比如微信登陆授权)

以下会着重介绍存储权限,定位权限和设备标识符三种权限的变更与适配。

存储权限

Android Q 在外部存储设备中为每个应用提供了一个“隔离存储沙盒”(例如 /sdcard)。任何其他应用都无法直接访问您应用的沙盒文件。由于文件是您应用的私有文件,因此您不再需要任何权限即可在外部存储设备中访问和保存自己的文件。此变更可让您更轻松地保证用户文件的隐私性,并有助于减少应用所需的权限数量。

沙盒,简单而言就是应用专属文件夹,并且访问这个文件夹无需权限。谷歌官方推荐应用在沙盒内存储文件的地址为Context.getExternalFilesDir()下的文件夹。比如要存储一张图片,则应放在Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)中。

以下将按访问的目标文件的地址介绍如何适配。

1. 访问自己文件:Q中用更精细的媒体特定权限替换并取消了 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限,并且无需特定权限,应用即可访问自己沙盒中的文件。

2. 访问系统媒体文件:Q中引入了一个新定义媒体文件的共享集合,如果要访问沙盒外的媒体共享文件,比如照片,音乐,视频等,需要申请新的媒体权限:READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,申请方法同原来的存储权限。

3. 访问系统下载文件:对于系统下载文件夹的访问,暂时没做限制,但是,要访问其中其他应用的文件,必须允许用户使用系统的文件选择器应用来选择文件。

4. 访问其他应用沙盒文件:如果你的应用需要使用其他应用在沙盒内创建的文件,请点击使用其他应用的文件,本文不做介绍。

所以请判断当应用运行在Q平台上时,取消对READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE两个权限的申请。并替换为新的媒体特定权限。

关于存储权限的(如何启用)影响范围

模拟器

在Android Q Beat1中,谷歌暂未开放存储权限的改动。我们需要使用adb命令

adb shell sm set-isolated-storage on

来开启模拟器对于存储权限的变更来进行适配。

真机

当满足以下每个条件时,将开启兼容模式,即不开启Q设备中的存储权限改动:

  1. 应用targetSDK<=P。

  2. 应用安装在从 Android P 升级到 Android Q 的设备上。

但是当应用重新安装(更新)时,不会重新开启兼容模式,存储权限改动将生效。

所以按官方文档所说,无论targetSDK是否为Q,必须对应用进行存储权限改动的适配。

在我的测试中,当targetSDK<=P,在Q Beat1版上申请两个旧权限时会自动改成申请三个新权限,不会影响应用正常使用,但当targetSDK==Q时,申请旧权限将失败并影响应用正常使用。

定位权限

为了让用户更好地控制应用对位置信息的访问权限,Android Q 引入了新的位置权限 ACCESS_BACKGROUND_LOCATION。

与现有的 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 权限不同,新权限仅会影响应用在后台运行时对位置信息的访问权。除非应用的某个 Activity 可见或应用正在运行前台服务,否则应用将被视为在后台运行。

与iOS系统一样,Q中也加入了后台位置权限ACCESS_BACKGROUND_LOCATION,如果应用需要在后台时也获得用户位置(比如滴滴),就需要动态申请ACCESS_BACKGROUND_LOCATION权限。

当然如果不需要的话,应用就无需任何改动,且谷歌会按照应用的targetSDK作出不同处理:

targetSDK <= P 应用如果请求了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限,Q设备会自动帮你申请ACCESS_BACKGROUND_LOCATION权限。

设备唯一标识符

从 Android Q 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 签名权限才能访问设备的不可重置标识符(包含 IMEI 和序列号)。

许多用例不需要不可重置的设备标识符。如果您的应用没有该权限,但您仍尝试查询标识符的相关信息。会返回空值或报错。

设备唯一标识符需要特别注意,原来的READ_PHONE_STATE权限已经不能获得IMEI和序列号,如果想在Q设备上通过

((TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId()

获得设备ID,会返回空值(targetSDK<=P)或者报错(targetSDK==Q)。且官方所说的READ_PRIVILEGED_PHONE_STATE权限只提供给系统app,所以这个方法算是废了。

谷歌官方给予了设备唯一ID最佳做法,但是此方法给出的ID可变,可以按照具体需求具体解决。

本文给出一个不变和基本不重复的UUID方法。

public static String getUUID() {String serial = null;String m_szDevIDShort = "35" +Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +Build.USER.length() % 10; //13 位try {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {serial = android.os.Build.getSerial();} else {serial = Build.SERIAL;}//API>=9 使用serial号return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {//serial需要一个初始化serial = "serial"; // 随便一个初始化
}//使用硬件信息拼凑出来的15位号码return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}

虽然由于唯一标识符权限的更改会导致android.os.Build.getSerial()返回unknown,但是由于m_szDevIDShort是由硬件信息拼出来的,所以仍然保证了UUID的唯一性和持久性。

经测试上述方法完全相同的手机有可能重复,网上还有其他方案比如androidID,但是androidID可能由于机型原因返回null,所以个人任务两种方法半斤八两。设备ID的获取一个版本比一个版本艰难,如果有好的方法欢迎指出。

minSDK警告

在 Android Q 中,当用户首次运行以 Android 6.0(API 级别 23)以下的版本为目标平台的任何应用时,Android平台会向用户发出警告。

如果此应用要求用户授予权限,则系统会先向用户提供调整应用权限的机会,然后才会允许此应用首次运行。

谷歌要求运行在Q设备上的应用targetSDK>=23,不然会向用户发出警告。

3.Q 行为变更:以 Android Q 为目标平台的应用

非 SDK 接口限制

非SDK接口限制在Android P中就已提出,但是在Q中,被限制的接口的分类有较大变化。

非SDK接口介绍

为了确保应用稳定性和兼容性,Android 平台开始限制您的应用可在 Android 9(API 级别 28)中使用哪些非 SDK 接口。Android Q 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。

非SDK接口限制就是某些SDK中的私用方法,如private方法,你通过Java反射等方法获取并调用了。那么这些调用将在target>=P或target>=Q的设备上被限制使用,当你使用了这些方法后,会报错:

获取方法

报错信息

Dalvik instruction referencing a field

NoSuchFieldError thrown

Dalvik instruction referencing a method

NoSuchMethodError thrown

Reflection via Class.getDeclaredField() or Class.getField()

NoSuchFieldException thrown

Reflection via Class.getDeclaredMethod(), Class.getMethod()

NoSuchMethodException thrown

Reflection via Class.getDeclaredFields(), Class.getFields()

Non-SDK members not in results

Reflection via Class.getDeclaredMethods(), Class.getMethods()

Non-SDK members not in results

JNI via env->GetFieldID()

NULL returned, NoSuchFieldError thrown

JNI via env->GetMethodID()

NULL returned, NoSuchMethodError thrown

非SDK接口查找

如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试该应用进行确认。

当你调用了非SDK接口时,会有类似Accessing hidden XXX的日志:

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

但是一个大项目到底哪里使用了这些方法,靠review代码和看日志肯定是不现实的,谷歌官方也提供了官方检查器veridex用来检测一个apk中哪里使用了非SDK接口。veridex下载。

https://android.googlesource.com/platform/prebuilts/runtime/+/master/appcompat

其中有windows,linux和mac版本,对应下载即可。

下载解压后命令行cd到veridex目录下使用./appcompat.sh --dex-file=Q.apk即可自动扫描。Q.apk为包的绝对路径,如果包与veridex在相同目录下直接输入包文件名即可。

扫描结果分为两部分,一部分为被调用的非SDK接口的位置,另一部分为非SDK接口数量统计,例如:

  1. greylist: 灰名单,即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口

  2. blacklist:黑名单,使用了就会报错。也是我们项目中必须解决的非SDK接口

  3. greylist-max-o:在targetSDK<=O中能使用,但是在targetSDK>=P中被限制的非SDK接口

  4. greylist-max-p:在targetSDK<=P中能使用,但是在targetSDK>=Q中被限制的非SDK接口

所以从适配Q的角度出发,除了greylist我们可以暂时不解决以外,其余三种类型的非SDK接口需要我们进行适配。

非SDK接口适配

如果您的应用依赖于非 SDK 接口,则应该开始计划迁移到 SDK 替代方案。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,则应该请求新的公共 API。

官方要求targetSDK>=P的应用不使用这些方法,并寻找其他的公共API去替代这些非SDK接口,如果找不到,则可以向谷歌申请,请求一个新的公共API https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces#feature-request (一般不需要)。

就我个人扫描并定位的结果来看,项目中使用非SDK接口大概率有以下两种情况:

  1. 在自定义View的过程中为了方便,使用反射修改某个参数。

  2. 三方SDK中使用了非SDK接口(这种情况比较多)。

第一种是好解决的,毕竟是我们自己写的代码。

第二种就头疼了,只能更新到最新的三方SDK版本,或者提工单、换库(也是整个适配过程中工作量最庞大的部分)。

4.项目升级遇到的问题

模拟器X86,项目中SO库为v7

  • 找到so库源代码,编译成x86

  • 如果so库只是某个功能点使用,对APP整体没大影响,就可以屏蔽特定so库功能或略过测试

  • 如果so库是项目核心库必须加载,也可使用腾讯云测,上面有谷歌亲儿子Q版本。腾讯云测有adb远程连接调试功能(我没成功过)。adb连不上也没关系,直接安装就行,云测上也可以直接看日志。

  • 至于inter的houdini我尝试研究过,理论上能安装在x86模拟器上让它编译v7的so库,但是由于关于houdini的介绍比较少也比较旧,建议大家时间不充裕的话就别研究了。

Requires development platform Q but this is a release platform.

由于目前Q是preview版,所以targetSDK==Q 的应用只能在Q设备上跑。

INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2

这个错误是由于打包压缩so库时造成的,具体原因可见

https://issuetracker.google.com/issues/37045367

在AndroidManifest.xml的application节点下加入android:extractNativeLibs="true"

可能有人加了上面代码还是不行,在app/build.gradle中的defaultConfig节点下加入

packagingOptions{ doNotStrip "/armeabi/.so" doNotStrip "/armeabi-v7a/.so" doNotStrip "/x86/.so" }

Didn't find class “org.apache.http.client.methods.HttpPost"

在AndroidManifest.xml的application节点下加入

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

如果你的项目没有适配过android O或P,那么你需要注意:

  1. android O的读取已安装应用权限(对应用内自动更新有影响)

  2. android P的默认禁止访问http的API

这两个版本的适配问题本文就不做详述,网上有很多详细的介绍。

总结

适配还是不能拉下,如果你一下子从6.0升级到Q,你真的会哭的。

平时也多注意三方库的更新,因为安卓版本的更新势必导致了需要更新三方库。

官方文档的永远是最准确的。

参考文献

官方文档

https://developer.android.com/preview

非SDK接口

https://juejin.im/post/5afe50eef265da0b70262463

----------  END  ----------

说句题外话,有不少人想加鱼哥微信,鱼哥姑且放出来,但是坑位有限哦

 推荐阅读

Java线程基础回顾及内存模型,看你还记得多少?

那些年,让我面试头大的几个排序算法,今天终于搞懂了!

刷了一个半月算法题,我薪资终于Double了

你的 APP 为何启动那么慢?

喜欢就点个在看吧

小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!相关推荐

  1. Android 10.0相关权限的变更(使用WiFi Direct必须打开定位)

    最近很多厂商推送了Android10.0 的ROM.Android之后,大量的App出现了闪退或者打不开,白屏等问题. Pangu-Immortal (Pangu-Immortal) · GitHub ...

  2. Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

    摘要:PackageManagerService是Android系统核心服务之一,在Android中的非常重要,主要负责APK.jar包等的管理. 阅读本文大约需要花费50分钟. 文章的内容主要还是从 ...

  3. 华为内测基于Android 10.0的EMUI 10系统;2019年Q1真无线耳机市场份额,苹果占半壁江山……...

    关注并标星星CSDN云计算 极客头条:速递.最新.绝对有料.这里有企业新动.这里有业界要闻,打起十二分精神,紧跟fashion你可以的! 每周三次,打卡即read 更快.更全了解泛云圈精彩news g ...

  4. 还在期待安卓9.0吗?Android 10.0要来了

    目前,美国 Google公司的 AndroidP (安卓9.0),已经正式全面推出有几个多月了.众多手机品牌厂商也都在积极的进行更新适配 Android 9.0 系统(修改UI界面也算是二次开发,嗯) ...

  5. 【高通SDM660平台 Android 10.0】(13) --- Camera ISP 之 数字成像系统介绍

    [高通SDM660平台 Android 10.0] --- Camera ISP 之 数字成像系统介绍 一.成像系统的组成 1.1 视角 1.2 曝光 1.3 感光度 ISO 1.4 光源 1.5 光 ...

  6. Mokee android 10.0代码下载编译总结

    Mokee代码下载 mokee下载 mokee编译 过程问题 做android framework开发也有很多年了,一直都是在公司里面下载编译,最近因为自己有个小需求想要实现,因此想着自己下载一个ao ...

  7. Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

    摘要:上一节讲解了PKMS的 权限扫描,扫描/system/etc/permissions中的xml,存入相应的结构体中,供之后权限管理使用. 这一节主要来讲讲APK的扫描. 阅读本文大约需要花费15 ...

  8. Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

    摘要:PackageManagerService在systemReady()后,进行了/system/etc/permissions中的各种xml进行扫描,进行相应的权限存储,供以后使用 阅读本文大约 ...

  9. Android 10.0 系统服务之ActivityMnagerService-AMS启动流程-[Android取经之路]

    摘要:上一节我们讲完了SystemServer的启动过程,这一节接着上一节的步骤,来讲解ActivityManagerService的启动过程. ActivityManagerService简称AMS ...

最新文章

  1. Pytorch nn.Transformer的mask理解
  2. java判断文件或者文件夹
  3. innovation
  4. SAP CRM呼叫中心toolbar的cancel按钮会导致session清理
  5. 杂项:E-Learning
  6. Android怎么自定义listview布局,Android ListView自定义布局
  7. idea+JRebel实现项目热部署
  8. oracle+buffer+busy+wait,Oracle数据库buffer busy wait等待事件 (2)
  9. RxJAVA notes
  10. 怎么用EasyRecovery把文件夹中丢失的数据恢复
  11. 使用Jsoup完成网页爬虫
  12. 翻译:YOLOv5 新版本——改进与评估
  13. 使用 HTML 和 CSS 创建响应式猫猫图片库
  14. .net reactor 学习系列(五)---.net reactor针对De4Dot脱壳工具的应对
  15. 为WIN7原生2019版ISO更改为中文
  16. 学习日记——Quartus工程创建与运行
  17. ARM开发工具综述转
  18. steam好友服务器当前处于离线状态,steam怎么修改在线状态-steam设置离线、想交易、隐身等状态的方法 - 河东软件园...
  19. Linux 锁机制(3)之自旋锁
  20. html整体垂直居中,实现HTML元素垂直居中的六种方法

热门文章

  1. 移动磁盘显示无法访问设备未就绪,里面的资料如何找到
  2. ObjectARXWizards AutoCAD .NET Wizards 下载地址
  3. android rom包的组成结构,AndroidROM的制作与结构构成..doc
  4. 微软开源!世界首个AI量化投资平台 Qlib 基本使用教程
  5. linux文件目录管理
  6. IntelliJ IDEA 中如何导入jar包(以引入spring包,构建spring测试环境为例)
  7. 【台大郭彦甫】Matlab入门教程超详细学习笔记七:数值微积分(附PPT链接)
  8. PHP单例模式demo
  9. Linux arping :向相邻主机发送ARP请求
  10. 纵观网络安全攻击的各种方式方法