所以按官方文档所说,无论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
,不然会向用户发出警告。
Q 行为变更:以 Android Q 为目标平台的应用
非 SDK 接口限制
非SDK接口限制在Android P中就已提出,但是在Q中,被限制的接口的分类有较大变化。
为了确保应用稳定性和兼容性,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接口时,会有类似Accessing hidden XXX
的日志:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
但是一个大项目到底哪里使用了这些方法,靠review
代码和看日志肯定是不现实的,谷歌官方也提供了官方检查器veridex
用来检测一个apk中哪里使用了非SDK接口。veridex下载。
其中有windows
,linux
和mac
版本,对应下载即可。下载解压后命令行cd
到veridex
目录下使用./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 接口的替代方案,则应该请求新的公共 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设备上跑。
这个错误是由于打包压缩so库时造成的,具体原因可见 issuetracker.google.com/issues/3704…
在AndroidManifest.xml的application节点下加入android:extractNativeLibs=“true”
可能有人加了上面代码还是不行,在app/build.gradle中的defaultConfig节点下加入
packagingOptions{ doNotStrip “/armeabi/.so” doNotStrip “/armeabi-v7a/.so” doNotStrip “/x86/.so” }
最后
那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。
通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!
北京 Android 工程师「工资收入水平 」
今天重点内容是怎么去学,怎么提高自己的技术。
1.合理安排时间
2.找对好的系统的学习资料
3.有老师带,可以随时解决问题
4.有明确的学习路线
当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!