Android Q 适配指南 让你少走一堆弯路

本文介绍了AndroidQ对存储权限、媒体文件访问、位置权限以及设备唯一标识符的新规定,强调了非SDK接口限制的变化,并提供了针对这些变更的适配策略和注意事项,包括模拟器设置和targetSDK对权限处理的影响。
摘要由CSDN通过智能技术生成

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

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

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

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

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

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

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

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

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

  • 模拟器

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

adb shell sm set-isolated-storage on

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

  • 真机

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

应用targetSDK<=P。
应用安装在从 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_LOCATIONACCESS_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,不然会向用户发出警告。

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


非 SDK 接口限制

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

  • 非SDK接口介绍

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

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

获取方法报错信息
Dalvik instruction referencing a fieldNoSuchFieldError thrown
Dalvik instruction referencing a methodNoSuchMethodError 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下载

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

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • greylist: 灰名单,即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口
  • blacklist:黑名单,使用了就会报错。也是我们项目中必须解决的非SDK接口
  • greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被限制的非SDK接口
  • greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被限制的非SDK接口

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

  • 非SDK接口适配

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

官方要求targetSDK>=P的应用不使用这些方法,并寻找其他的公共API去替代这些非SDK接口,如果找不到,则可以向谷歌申请,请求一个新的公共API(一般不需要)。

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

  • 在自定义View的过程中为了方便,使用反射修改某个参数。
  • 三方SDK中使用了非SDK接口(这种情况比较多)。

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

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

项目升级遇到的问题


  • 模拟器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设备上跑。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-LktQBL06-1713723151591)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值