前语
Android官方的搬家适配文档有点紊乱,这篇文章旨在给开发者在适配中对代码做快速检查。适配改动将分为运转版别影响和Target版别影响,并供给或许影响的功用以便检验参阅。转载请注明来历「Bug总柴」
Android Q (API level 29)
沙箱机制(scoped-storage)
在Android Q中改动比较大的是对外置sdcard的访问权限改动,这个改动将会影响大部分需求访问外置存储的运用。
沙箱机制解读
- 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/ 三个文件夹。
- READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE的通用访问外置sdcard的权限被拆分为访问音乐READ_MEDIA_AUDIO、相片READ_MEDIA_IMAGES和视频READ_MEDIA_VIDEO三种权限,而访问运用沙箱的内容无需额外央求权限。
沙箱收效机遇
- 假设target版别小于等于28并且运用是设备在从Android 9升级到Andoid Q的手机上,则会启用兼容形式,依然可以随意访问external存储的内容。
- 意味着当target版别大于28,或许运用是在Android Q的手机上新设备都会使沙箱机制收效。这儿需求说明,不管是否target到28以上,只要是在Android Q上新设备的运用都会使沙箱机制收效。
- 关于模拟器里面的Andorid Q Beta 1版别,需求履行adb shell sm set-isolated-storage on敞开沙箱机制
影响规模
- 各种为了完结离线运用功用的离线下载文件
- 各种缓存文件(例如信息流缓存、广告缓存等)
- 需求注意某些三方库或许会运用外置sdcard(例如log或许crash核算等)
四、处理方法
- 关于图片视频音乐和下载文件可以通过MediaStore类访问,或许运用Storage Access Framework
- 关于之前存储在外置sdcard的其他数据,需求搬家存储到getExternalFilesDir目录中
- 关于新增的文件尽量保存在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_STATE 或TelephonyManager.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定位功用 |
TelephonyManager 中getAllCellInfo() 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、 GnssMeasurement、GnssNavigationMessage、 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_PHONE、TYPE_PRIORITY_PHONE、 TYPE_SYSTEM_ALERT、TYPE_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_PHONE、TYPE_PRIORITY_PHONE、 TYPE_SYSTEM_ALERT、TYPE_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() 都会报错 |
集结排序 |