Android Target SDK升级一路趟坑记

target SDK升级是一个老生常谈的问题,由于总所周知的原因,很多国内APP的Target SDK版本号还停留在23以前,甚至是19。本文结合项目中实际遇到的问题,简单的梳理一下升级Target SDK遇到的问题。

来自Google Play官方的要求:

Improving app security and performance on Google Play for years to come

同时,国内也终于迎来​工信部的要求,手机出厂预置的所有应用android:targetSdkVersion>=26。

这里简单梳理一下从API21->API28的升级工作吧。

Android 5.0 & 5.1

Android 5.0 行为变更

关于Android 5.0和5.1(API 21和API 22),最深的影响就是ART取代Dalvik,成为平台默认设置.

不再支持隐式intent去bindService()

Target SDK部分的变更不多,影响最大的变更可能是不再支持隐式intent去bindService()的方式,简单说就是bindService的intent,不光需要指定Action,还需要指定Service的PackageName,不然系统会报错。

其他的没什么多说的,遇到问题可以自行查看官方文档。


Android 6.0

Android 6.0 行为变更

权限申请

关于Android 6.0(API 23),最值得一提的也就是运行时权限了。这也是很多国内APP迟迟不愿意升级Target SDK的最主要原因,也是我们升级过程中最需要处理的问题。首先明确一点,仅被Android认定为『危险权限』,才需要使用前申请。然后,危险权限根据系统认定,具有以下四种不同的状态:

  1. 有权限
  2. 需要询问
  3. 仅拒绝
  4. 拒绝+不再提醒

如果权限被拒绝过,下次申请时建议弹窗说明理由。如果用户设置不再提醒,又确实需要,可以引导用户去设置里手动开启。其他的就不多说了,这里给两个比较好的权限库。

一个是google的开源库方案,googlesamples/easypermissions,优点是google出品,代码风格和质量都有保证。不过缺点也很明显,调用方式依赖Activity和Fragment,而项目中很多代码不可能从头改造。

另一个是我强烈推荐的国内的一个开源库方案,yanzhenjie/AndPermission,star数4000+,依赖context+流式调用,同时兼容国内的各类机型,非常好用。


Android 7.0 & 7.1.1

Android 7.0 行为变更

关于Android 7.0和7.1.1(API 24和API 25),也有很多需要开发者注意的地方。

User-added CAs not trusted by default for secure connections

首先是 User-added CAs not trusted by default for secure connections, 也就是说,如果开发者没有配置的话,通过charles等抓包软件,在仅导入用户证书的情况下,是没有办法抓https请求的包的。(如果有https系统的证书,还是可以抓包的)。

具体可以参考 Android 7.1 以上机型Charles抓包解决办法

处理方式其实也很简单,假如期待的是Dev渠道的APK安装包可以信任用户证书,实现抓包,则新建一个xml文件:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>

然后放在Applicaiton所在模块下的src/Dev/res/xml目录下

应用私有目录被限制访问

(系统权限更改-应用私有目录被限制访问)[https://developer.android.com/about/versions/nougat/android-7.0-changes#permfilesys]

简单来说,影响有以下几点:

  • 外部App被限制访问App的私有目录。尝试的话将触发SecurityException
  • 传递 file:// URI 会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider

具体内容可以参考这篇博文:
关于 Android 7.0 适配中 FileProvider 部分的总结

举两个具体受影响的业务场景:

调起系统相机拍照

之前调起系统拍照的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//省略检测相机,权限申请...
File file = new File(imagePath);
Uri uri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, REQUEST_CODE_CAPTURE);
}

@Override
public void onActivityResult(int requestCode, int resultCode,Intent data) {
if(requestCode == REQUEST_CODE_CAPTURE) {
//判断是否成功
}
}

如果在android 7.0以上的机型,则会发现resultCode返回Activity.RESULT_CANCELED,也就是0。处理起来也很简单,Android Support库为我们提供了工具方法:

1
2
3
4
5
6
7
8
9
10
11
12
public void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//省略检测相机,权限申请...
File file = new File(imagePath);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
tempImageUri = FileProvider.getUriForFile(this, "fileprovider的包名", file);
} else {
Uri uri = Uri.fromFile(file);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, REQUEST_CODE_CAPTURE);
}

这样即可正常拍照了。

安装apk

Android 6.0 7.0 8.0三个版本Install Apk 采坑记录

这篇博文说的很清楚,这里就不多说了。


Android 8.0

Android 8.0 行为变更

访问用户账户需要授权

首当其冲的是如果没有用户的许可,App无法直接访问Android的系统用具账户。什么意思?在Android系统中,App可以通过AccountManager类,创建和访问用户账户.如Android系统的Google账户,小米手机里面的小米账户等等。此前App仅需有如下权限:

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

但Target 26的App,非系统签名的应用则无法获取到系统帐号.原生的系统AccountManager机制提供了两种方式使这些app来获取:

  • 由authenticator app或是跟authenticator app相同签名的其他app调用setAccountVisibility(Account account, String packageName, int visibility)来将指定的app设置为帐号可见
  • 没有权限的app自己调用newChooseAccountIntent方法来由用户来选择是否允许该app访问对应帐号

方案一般来说用户体验更加好,方案二的原生页面会让用户很难费解.这里就不做具体展开了。

提醒窗口

一些App需要在其他应用和系统窗口上方显示提醒窗口,Target 26的App,使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来显示提醒窗口了:

TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR

替换方案是使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型.新窗口有一些特性,更多的可以在这里进行了解 提醒窗口


Android 9.0

以 API 级别 28+ 为目标的应用

前台服务

引用官方文档:

针对 Android 9 或更高版本并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。 这是普通权限,因此,系统会自动为请求权限的应用授予此权限。
如果针对 Android 9 或更高版本的应用尝试创建一个前台服务且未请求 FOREGROUND_SERVICE,则系统会引发 SecurityException。

(更多内容,待续)