Andriod版本适配 check list(持续更新)

前言

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/<package> , /storage/emulated/0/Android/media/<package> , /storage/emulated/0/Android/obb/<package> 三个文件夹。
  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/<package>/files
Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) -> /storage/emulated/0/Android/data/<package>/files/Pictures
Context.externalCacheDir -> /storage/emulated/0/Android/data/<package>/cache
Context.obbDir -> /storage/emulated/0/Android/obb/<package>
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配置实现
例如软件安装后的广播处理以及网络变化通知处理功能
后台应用获取位置受到限制,包括FusedLocationProviderApi
GnssMeasurementGnssNavigationMessage
WifiManager.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_PHONE
TYPE_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_PHONE
TYPE_SYSTEM_ALERTTYPE_SYSTEM_OVERLAY
TYPE_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()都会报错 集合排序

推荐阅读更多精彩内容