targetSdkVersion升级到28一些修改的地方

TOC

前言

Android官方的迁移适配文档有点混乱,这篇文章旨在给开发者在适配中对代码做快速检查。适配变化将分为运行版本影响和Target版本影响,并提供可能影响的功能以便测试参考。转载请注明来源「Bug总柴」

Android Q (API level 29)

沙箱机制(scoped-storage)

在Android Q中变化比较大的是对外置sdcard的访问权限变化,这个变化将会影响大部分需要访问外置存储的应用。

沙箱机制解读

  1. external storage在Android Q开始被设置成像internal storage那种只能访问自己包名下的空间,无法直接访问sdcard其他位置内容。就算声明了READ_EXTERNAL_STORAGE权限,在应用中通过File.listFiles只能看到/storage/emulated/0/Android/data/ , /storage/emulated/0/Android/media/ , /storage/emulated/0/Android/obb/ 三个文件夹。
  2. READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE的通用访问外置sdcard的权限被拆分为访问音乐READ_MEDIA_AUDIO、照片READ_MEDIA_IMAGES和视频READ_MEDIA_VIDEO三种权限,而访问应用沙箱的内容无需额外申请权限。

沙箱生效时机

  1. 如果target版本小于等于28并且应用是安装在从Android 9升级到Andoid Q的手机上,则会启用兼容模式,仍然可以随意访问external存储的内容。
  2. 意味着当target版本大于28,或者应用是在Android Q的手机上新安装都会使沙箱机制生效。这里需要说明,不管是否target到28以上,只要是在Android Q上新安装的应用都会使沙箱机制生效。
  3. 对于模拟器里面的Andorid Q Beta 1版本,需要执行adb shell sm set-isolated-storage on开启沙箱机制

影响范围

  1. 各种为了实现离线使用功能的离线下载文件
  2. 各种缓存文件(例如信息流缓存、广告缓存等)
  3. 需要注意某些三方库可能会使用外置sdcard(例如log或者crash统计等)

四、处理办法

  1. 对于图片视频音乐和下载文件可以通过MediaStore类访问,或者使用Storage Access Framework
  2. 对于之前存储在外置sdcard的其他数据,需要迁移存储到getExternalFilesDir目录中
  3. 对于新增的文件尽量保存在getExternalFilesDir和getExternalCacheDir

Api检查

Context.getExternalFilesDir(null) -> /storage/emulated/0/Android/data//files Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) -> /storage/emulated/0/Android/data//files/Pictures Context.externalCacheDir -> /storage/emulated/0/Android/data//cache Context.obbDir -> /storage/emulated/0/Android/obb/ Environment.getExternalStorageDirectory() -> /storage/emulated/0 (沙箱机制下无法访问) Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) -> /storage/emulated/0/Pictures (沙箱机制下无法访问)

Android 9 (API level 28)

官方行为变更文档

非SDK接口使用限制

使用 veridex工具测试apk是否有调用非SDK接口

➜  veridex-mac ./appcompat.sh --dex-file=test.apk

实例结果如下:

6889 hidden API(s) used: 6817 linked against, 72 through reflection
       0 in blacklistgetConnectionInfo
       3 in dark greylist
       47 in light greylist
To run an analysis that can give more reflection accesses, 
but could include false positives, pass the --imprecise flag.

其中:

类型 描述
blacklist 不管是否target到28,都会报NoSuchMethodError/NoSuchFieldException
dark greylist 如果target在28一下没问题,但是target到28及以上会报NoSuchMethodError/NoSuchFieldException
light greylist 暂时没有问题,可以使用

处理办法:
去除blacklist以及dark greylist的非android sdk调用的反射调用,有些是android support包内部调用的可以考虑升级support包版本

隐私&权限相关

运行在9.0受到影响 可能受到影响的功能
不能在后台访问麦克风和摄像头 后台录音、后台拍照
加速器陀螺仪等传感器不能在后台持续获取数据 步数计算
通过变化模式或者单次模式的传感器收不到事件 显著运动检测、计步器、近程传感器和心率传感器
通话记录权限组别由PHONE组调整到CALL_LOG 需要获通过记录权限的功能
通过android.intent.action.PHONE_STATETelephonyManager.listen方法获取手机号码需要申请READ_CALL_LOG 权限 例如来电归属地显示或者来电拦截等需要获取通话手机号的功能
wifi扫描频率限制更为严格,getConnectionInfo WifiManager.getScanResults()以及 WifiManager.startScan()需要而外权限详见 需要wifi扫描匹配等功能
WifiManager.getConnectionInfo() 要获得SSID和BSSID,要求定位权限并要求设备打开定位功能,NETWORK_STATE_CHANGED_ACTION 不再能获得SSID和BSSID 需要获取wifi信息的功能
WifiManager与WifiP2pManager中getScanResults() getConnectionInfo()discoverServices() addServiceRequest()NETWORK_STATE_CHANGED_ACTION不再包含用户定位信息 使用wifi定位功能
TelephonyManagergetAllCellInfo() listen() getCellLocation() getNeighboringCellInfo()不返回结果,除非用户打开了定位功能 使用移动信号定位
Target在9.0受到影响 可能受到影响的功能
启动前台服务要去注册android.permission.FOREGROUND_SERVICE权限 前台服务启动
获取序列号不能通过Build.SERIAL,需要注册android.permission.READ_PHONE_STATE然后使用Build.getSerial() 获取序列号相关功能

安全相关

运行在9.0受到影响 可能受到影响的功能
SSLSocket出错不返回NullPointerException,改成返回IOException https网络错误处理
加密函数Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC") Cipher.getInstance("AES/CBC/PKCS7PADDING",Security.getProvider("BC")) SecureRandom.getInstance("SHA1PRNG", "Crypto");移除 加密功能
Android secure encrypted files移除 移动app到sdcard功能
Target在9.0受到影响 可能受到影响的功能
DNS客户端需要根据系统使用加密DNS查找与系统相同的主机名,或改由系统解析程序 DNS自解析功能
默认要求使用https,如果需要使用http需要设置cleartextTrafficPermitted="true"详见 所有http网络请求
webview的数据包括cookies和caches不允许多进程共享 多进程使用webview
不用通过设置全局Unix权限共享数据文件,不用应用的文件共享需要使用ContentProvider 应用间文件共享

国际化相关

运行在9.0受到影响 可能受到影响的功能
java.text.SimpleDateFormat 使用zzzz格式、java.text.DateFormatSymbols.getZoneStrings()格式、NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)格式修改 时区、货币显示相关功能

网络相关

运行在9.0受到影响 可能受到影响的功能
NetworkCapabilities支持返回NET_CAPABILITY_NOT_VPN vpn设置功能
Apache HTTP client不能使用system ClassLoader加载,若要使用需要实现自定义ClassLoader 使用旧Apache Http client网络功能
Target在9.0受到影响 可能受到影响的功能
NetworkStatsManager 能获取非当前正在使用的流量情况 网络使用统计
ConnectivityManager.getMultipathPreference() 可以获取是否超过了移动流量使用限制 网络使用情况提醒
Apache Http背去除,要使用需要加上<uses-library android:name="org.apache.http.legacy" android:required="false"/>或者想apache.http相关类包通过jar方式引入 使用旧Apache Http client网络功能

界面相关

运行在9.0受到影响 可能受到影响的功能
通过非activity的context启动activity强制要求intent带上FLAG_ACTIVITY_NEW_TASK 后台启动页面
屏幕旋转方式由原来的“自动旋转”和“纵向”改为“自动旋转”和“固定旋转” 屏幕旋转功能
Target在9.0受到影响 可能受到影响的功能
长或宽为0的view不再可以获取焦点,新开页面不默认获取焦点 交互过程通过特殊焦点实现的功能
webview可以支持带透明度的8位颜色css webview css 颜色透明度功能
webview中document的root元素滚动位置得到支持 webview 相关
暂停挂起app的通知会在app resumed之后重新通知 通知相关

设备相关

运行在9.0受到影响 可能受到影响的功能
多摄像头支持getCameraIdList()前后摄像头切换需要选择合适的摄像头 摄像头相关功能

其他

运行在9.0受到影响 可能受到影响的功能
UTF-8解码更加严格按照Unicode标准详见 UTF-8解码相关的功能

实用参考地址

权限组级别

Android 8 (API level 26)

官方行为变更文档

后台限制

运行在8.0受到影响 可能受到影响的功能
后台应用通过startService()方法启动服务,包括IntentService会受到限制并抛出IllegalStateException异常,需要改成使用 JobScheduler 或者JobIntentService 所有启动后台服务的行为,包括但不限于后台下载、后台数据更新、后台初始化等等
前台服务启动不能通过启动后台服务再将其转换为前台,需要通过startForegroundService()方法,并在5s内调用startForeground()方法显示前台通知,否则会ANR 所有前台服务,包括音乐播放功能、其他有通知的服务
自定义action广播以及其他系统非指向性的广播接收受到限制,可通过manifests注册指向性广播或者通过Context.registerReceiver()动态注册,系统性的广播事件可考虑通过JobScheduler配置实现 例如软件安装后的广播处理以及网络变化通知处理功能
后台应用获取位置受到限制,包括FusedLocationProviderApiGnssMeasurementGnssNavigationMessageWifiManager.startScan()LocationManager,需要使用前台服务保持应用前台状态 后台动作检测功能、后台需要用到地理位置的功能例如后台导航之类

隐私&权限相关

运行在8.0受到影响 可能受到影响的功能
ANDROID_ID从之前的仅与设备相关,改为与应用签名、设备、设备登录用户相关。 使用ANDROID_ID的功能
获取系统属性net.hostname将返回null wifi hostname获取功能
Target在8.0受到影响 可能受到影响的功能
系统属性net.dns*不再支持 通过系统属性获取dns功能
需要获取DNS信息需要ACCESS_NETWORK_STATE权限,通过NetworkRequest或者NetworkCallback获取 DNS获取功能
获取序列号不能通过Build.SERIAL,需要注册android.permission.READ_PHONE_STATE然后使用Build.getSerial() 获取序列号相关功能
LauncherApps获取不同用户的应用信息时,会当做没有任何应用安装,而不是抛出异常 桌面启动器相关功能
相同权限组的其他权限会在真正需要时才被自动授予,之前是整个权限组同时授予 权限授予相关

安全相关

运行在8.0受到影响 可能受到影响的功能
不再支持SSLv3 使用SSLv3的地方
当HTTPS使用错误的TLS协议与服务交互时,不再使用其他TLS协议重试 HTTPS相关
在bionic之外的系统调用将被禁止 bionic系统调用
WebView被运行在多进程空间 WebView间数据共享
APKs安装路径可能会被修改 APKs管理
判断是否能安装应用需使用PackageManager.canRequestPackageInstalls()INSTALL_NON_MARKET_APPS失效 应用安装
8.0系统默认禁止应用安装未知应用 应用安装功能
Thread.UncaughtExceptionHandler 会记录在stacktrace中,但不会杀死应用 线程异常处理
Target在8.0受到影响 可能受到影响的功能
registerContentObserver(Uri, boolean, ContentObserver)中的Uri必须使用ContentProvider注册 以Uri来通知变化的功能
network_security_config.xml 配置禁止明文传输将同样影响WebView Https功能
AccountManager不能只通过申明GET_ACCOUNTS来获取账号,需要调用AccountManager.newChooseAccountIntent()让用户选择,再通过AccountManager.getAccounts()来获取 Account Services相关
native库若包含可执行文件则不会加载 native库相关
JNI调用会检查反射的类或方法是否存在,否则会抛出异常 JNI调用
DexFile API已经过时,建议使用系统默认PathClassLoader 或者 BaseDexClassLoader如果需要用到DexFile,不应该进行压缩,否则会解压消耗内存。多线程加载相同类由最先加载的类的加载器决定。 Dex 加载相关

国际化相关

运行在8.0受到影响 可能受到影响的功能
Currency.getDisplayName()Currency.getSymbol()Locale.getDisplayScript()默认调用Locale.getDefault(Category.DISPLAY) 国际化显示
Currency.getDisplayName(null)将会抛出异常 国际化单位显示
对于SimpleDateFormat的时区获取由原来在设备第一次启动时候获取,改为每次实时获取 时区显示
升级ICU到58版本 国际化单位标准

网络相关

运行在8.0受到影响 可能受到影响的功能
无正文的 OPTIONS 请求具有 Content-Length: 0 头部 options请求相关
HttpURLConnection会保证请求最后带上“/” HttpURLConnection
ProxySelector.setDefault() 设置的代理仅处理scheme/host/port,不会处理请求参数 代理设置相关功能
不再支持空lable的URI 使用URI相关功能
HttpsURLConnection不会执行不安全的TLS/SSL协议版本回退 HttpsURLConnection
隧道Https协议改变,具体见Networking and HTTP(S) connectivity 隧道Https
如果DatagramSocket.connect() 返回错误,DatagramSocket.send()也会返回错误 socket相关
InetAddress.isReachable() 会在会退到TCP Echo协议之前尝试ICMP协议,若不可达会消耗更多时间 IP地址判断是否可达等网络功能
在支持设备上wifi连接当有强度大且已经保存的网络时可以自动切换 需保证网络切换不会影响应用功能

界面相关

运行在8.0受到影响 可能受到影响的功能
TYPE_PHONETYPE_PRIORITY_PHONETYPE_SYSTEM_ALERTTYPE_SYSTEM_OVERLAY TYPE_SYSTEM_ERROR这些类型的窗口都会显示在TYPE_APPLICATION_OVERLAY之下 悬浮球、快速查词等需要弹窗弹窗的地方
使用键盘导航时,获取焦点的view将会加上ripple高亮,如果不需要这种默认的高亮,需要设置android:defaultFocusHighlightEnabled或者setDefaultFocusHighlightEnabled(false) 键盘导航
webview中WebSettings.getSaveFormData()返回false,WebSettings.setSaveFormData()没有任何作用,WebViewDatabase.clearFormData()没有任何作用,WebViewDatabase.hasFormData()返回false 网页相关
Target在8.0受到影响 可能受到影响的功能
TYPE_PHONETYPE_PRIORITY_PHONETYPE_SYSTEM_ALERTTYPE_SYSTEM_OVERLAYTYPE_SYSTEM_ERROR不能用在alert window上,必须使用TYPE_APPLICATION_OVERLAY 悬浮球、快速查词等需要弹窗弹窗的地方
可点击的View默认拥有可获取焦点属性 View焦点显示
Notificaiton通知必须指定Notificaiton Channels,否则不会显示通知,详见notifications 通知相关

设备相关

运行在8.0受到影响 可能受到影响的功能
蓝牙ScanRecord.getBytes()返回长度不受限制 蓝牙相关功能
Target在8.0受到影响 可能受到影响的功能
音频获取焦点时会自动降低其他音频音量,现在支持暂停而不是降低音量,详见automatic ducking 音频播放相关功能
当来电时,自动静音音频播放 音频播放相关功能
需要使用AudioAttributes实现音频回放功能,AudioTrack过期 音频回放功能
音量按键事件会优先给前台activity,如果前台activity不处理会给最近一次播放音频的应用 音量控制

其他

运行在8.0受到影响 可能受到影响的功能
应用快捷方式不能通过com.android.launcher.action.INSTALL_SHORTCUT创建,需要使用ShortcutManager,具体如何创建可以看这篇文章 快捷方式创建功能
无障碍功能中双击动作转换为点击动作、能识别TextView中的ClickableSpan 无障碍功能
findViewById() 返回类型由View改为<T extends View> T 覆盖findViewById() 的地方需要相应修改
从2019年1月7日起,将无法通过LAST_TIME_CONTACTED/TIMES_CONTACTED/LAST_TIME_USED/TIMES_USED获取联系人使用情况 联系人联系情况获取功能
AbstractCollection.removeAll(java.util.Collection)/AbstractCollection.retainAll(java.util.Collection)当传入参数为null时会报NullPointerException 集合操作
Target在8.0受到影响 可能受到影响的功能
浏览器ua会包含OPR有可能导致判断是否Opera浏览器失效 根据ua判断浏览器
Collections.sort()改为在List.sort()基础上实现,之前是恰好相反。如果在List.sort()中调用Collections.sort()会产生死循环 集合排序
在遍历的过程中进行排序,现在使用无论使用List.sort()还是Collections.sort()都会报错 集合排序