前言
Android官方的遷移適配文檔有點混亂,這篇文章旨在給開發(fā)者在適配中對代碼做快速檢查芬迄。適配變化將分為運行版本影響和Target版本影響,并提供可能影響的功能以便測試參考。轉載請注明來源「Bug總柴」
Android Q (API level 29)
沙箱機制(scoped-storage)
在Android Q中變化比較大的是對外置sdcard的訪問權限變化靶橱,這個變化將會影響大部分需要訪問外置存儲的應用佳遂。
沙箱機制解讀
- external storage在Android Q開始被設置成像internal storage那種只能訪問自己包名下的空間营袜,無法直接訪問sdcard其他位置內(nèi)容。就算聲明了READ_EXTERNAL_STORAGE權限丑罪,在應用中通過File.listFiles只能看到/storage/emulated/0/Android/data/<package> , /storage/emulated/0/Android/media/<package> , /storage/emulated/0/Android/obb/<package> 三個文件夾荚板。
- READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE的通用訪問外置sdcard的權限被拆分為訪問音樂READ_MEDIA_AUDIO凤壁、照片READ_MEDIA_IMAGES和視頻READ_MEDIA_VIDEO三種權限,而訪問應用沙箱的內(nèi)容無需額外申請權限跪另。
沙箱生效時機
- 如果target版本小于等于28并且應用是安裝在從Android 9升級到Andoid Q的手機上拧抖,則會啟用兼容模式,仍然可以隨意訪問external存儲的內(nèi)容免绿。
- 意味著當target版本大于28唧席,或者應用是在Android Q的手機上新安裝都會使沙箱機制生效。這里需要說明嘲驾,不管是否target到28以上淌哟,只要是在Android Q上新安裝的應用都會使沙箱機制生效。
- 對于模擬器里面的Andorid Q Beta 1版本辽故,需要執(zhí)行adb shell sm set-isolated-storage on開啟沙箱機制
影響范圍
- 各種為了實現(xiàn)離線使用功能的離線下載文件
- 各種緩存文件(例如信息流緩存徒仓、廣告緩存等)
- 需要注意某些三方庫可能會使用外置sdcard(例如log或者crash統(tǒng)計等)
四、處理辦法
- 對于圖片視頻音樂和下載文件可以通過MediaStore類訪問榕暇,或者使用Storage Access Framework
- 對于之前存儲在外置sdcard的其他數(shù)據(jù)蓬衡,需要遷移存儲到getExternalFilesDir目錄中
- 對于新增的文件盡量保存在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是否有調(diào)用非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調(diào)用的反射調(diào)用,有些是android support包內(nèi)部調(diào)用的可以考慮升級support包版本
隱私&權限相關
運行在9.0受到影響 | 可能受到影響的功能 |
---|---|
不能在后臺訪問麥克風和攝像頭 | 后臺錄音缴啡、后臺拍照 |
加速器陀螺儀等傳感器不能在后臺持續(xù)獲取數(shù)據(jù) | 步數(shù)計算 |
通過變化模式或者單次模式的傳感器收不到事件 | 顯著運動檢測壁晒、計步器、近程傳感器和心率傳感器 |
通話記錄權限組別由PHONE 組調(diào)整到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網(wǎng)絡錯誤處理 |
加密函數(shù)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客戶端需要根據(jù)系統(tǒng)使用加密DNS查找與系統(tǒng)相同的主機名帮孔,或改由系統(tǒng)解析程序 | DNS自解析功能 |
默認要求使用https雷滋,如果需要使用http需要設置cleartextTrafficPermitted="true" 詳見
|
所有http網(wǎng)絡請求 |
webview的數(shù)據(jù)包括cookies和caches不允許多進程共享 | 多進程使用webview |
不用通過設置全局Unix權限共享數(shù)據(jù)文件,不用應用的文件共享需要使用ContentProvider | 應用間文件共享 |
國際化相關
運行在9.0受到影響 | 可能受到影響的功能 |
---|---|
java.text.SimpleDateFormat 使用zzzz 格式文兢、java.text.DateFormatSymbols.getZoneStrings() 格式晤斩、NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String) 格式修改 |
時區(qū)、貨幣顯示相關功能 |
網(wǎng)絡相關
運行在9.0受到影響 | 可能受到影響的功能 |
---|---|
NetworkCapabilities支持返回NET_CAPABILITY_NOT_VPN | vpn設置功能 |
Apache HTTP client不能使用system ClassLoader加載姆坚,若要使用需要實現(xiàn)自定義ClassLoader | 使用舊Apache Http client網(wǎng)絡功能 |
Target在9.0受到影響 | 可能受到影響的功能 |
---|---|
NetworkStatsManager 能獲取非當前正在使用的流量情況 |
網(wǎng)絡使用統(tǒng)計 |
ConnectivityManager.getMultipathPreference() 可以獲取是否超過了移動流量使用限制 |
網(wǎng)絡使用情況提醒 |
Apache Http背去除澳泵,要使用需要加上<uses-library android:name="org.apache.http.legacy" android:required="false"/> 或者想apache.http相關類包通過jar方式引入 |
使用舊Apache Http client網(wǎng)絡功能 |
界面相關
運行在9.0受到影響 | 可能受到影響的功能 |
---|---|
通過非activity的context啟動activity強制要求intent帶上FLAG_ACTIVITY_NEW_TASK
|
后臺啟動頁面 |
屏幕旋轉方式由原來的“自動旋轉”和“縱向”改為“自動旋轉”和“固定旋轉” | 屏幕旋轉功能 |
Target在9.0受到影響 | 可能受到影響的功能 |
---|---|
長或?qū)挒?的view不再可以獲取焦點,新開頁面不默認獲取焦點 | 交互過程通過特殊焦點實現(xiàn)的功能 |
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
|
所有啟動后臺服務的行為腊敲,包括但不限于后臺下載、后臺數(shù)據(jù)更新幢妄、后臺初始化等等 |
前臺服務啟動不能通過啟動后臺服務再將其轉換為前臺兔仰, 需要通過startForegroundService()方法, 并在5s內(nèi)調(diào)用startForeground()方法顯示前臺通知蕉鸳,否則會ANR |
所有前臺服務乎赴,包括音樂播放功能、其他有通知的服務 |
自定義action廣播以及其他系統(tǒng)非指向性的廣播接收受到限制潮尝, 可通過manifests注冊指向性廣播或者通過 Context.registerReceiver() 動態(tài)注冊榕吼,系統(tǒng)性的廣播事件可考慮通過 JobScheduler 配置實現(xiàn) |
例如軟件安裝后的廣播處理以及網(wǎng)絡變化通知處理功能 |
后臺應用獲取位置受到限制,包括FusedLocationProviderApi勉失、 GnssMeasurement羹蚣、GnssNavigationMessage、 WifiManager.startScan()乱凿、LocationManager顽素,需要使用前臺服務保持應用前臺狀態(tài) |
后臺動作檢測功能、后臺需要用到地理位置的功能例如后臺導航之類 |
隱私&權限相關
運行在8.0受到影響 | 可能受到影響的功能 |
---|---|
ANDROID_ID從之前的僅與設備相關徒蟆,改為與應用簽名胁出、設備、設備登錄用戶相關段审。 | 使用ANDROID_ID 的功能 |
獲取系統(tǒng)屬性net.hostname 將返回null |
wifi hostname獲取功能 |
Target在8.0受到影響 | 可能受到影響的功能 |
---|---|
系統(tǒng)屬性net.dns* 不再支持 |
通過系統(tǒng)屬性獲取dns功能 |
需要獲取DNS信息需要ACCESS_NETWORK_STATE 權限全蝶,通過NetworkRequest或者NetworkCallback獲取 |
DNS獲取功能 |
獲取序列號不能通過Build.SERIAL,需要注冊android.permission.READ_PHONE_STATE 然后使用Build.getSerial()
|
獲取序列號相關功能 |
LauncherApps獲取不同用戶的應用信息時寺枉,會當做沒有任何應用安裝抑淫,而不是拋出異常 | 桌面啟動器相關功能 |
相同權限組的其他權限會在真正需要時才被自動授予,之前是整個權限組同時授予 | 權限授予相關 |
安全相關
運行在8.0受到影響 | 可能受到影響的功能 |
---|---|
不再支持SSLv3 | 使用SSLv3的地方 |
當HTTPS使用錯誤的TLS協(xié)議與服務交互時姥闪,不再使用其他TLS協(xié)議重試 | HTTPS相關 |
在bionic之外的系統(tǒng)調(diào)用將被禁止 | bionic系統(tǒng)調(diào)用 |
WebView被運行在多進程空間 | WebView間數(shù)據(jù)共享 |
APKs安裝路徑可能會被修改 | APKs管理 |
判斷是否能安裝應用需使用PackageManager.canRequestPackageInstalls()始苇,INSTALL_NON_MARKET_APPS 失效 |
應用安裝 |
8.0系統(tǒng)默認禁止應用安裝未知應用 | 應用安裝功能 |
Thread.UncaughtExceptionHandler 會記錄在stacktrace中,但不會殺死應用 |
線程異常處理 |
Target在8.0受到影響 | 可能受到影響的功能 |
---|---|
registerContentObserver(Uri, boolean, ContentObserver)中的Uri必須使用ContentProvider 注冊 |
以Uri來通知變化的功能 |
network_security_config.xml 配置禁止明文傳輸將同樣影響WebView |
Https功能 |
AccountManager不能只通過申明GET_ACCOUNTS 來獲取賬號筐喳,需要調(diào)用AccountManager.newChooseAccountIntent()讓用戶選擇埂蕊, 再通過AccountManager.getAccounts()來獲取 |
Account Services相關 |
native庫若包含可執(zhí)行文件則不會加載 | native庫相關 |
JNI調(diào)用會檢查反射的類或方法是否存在,否則會拋出異常 | JNI調(diào)用 |
DexFile API已經(jīng)過時疏唾,建議使用系統(tǒng)默認PathClassLoader 或者 BaseDexClassLoader 。如果需要用到DexFile函似,不應該進行壓縮槐脏,否則會解壓消耗內(nèi)存。 多線程加載相同類由最先加載的類的加載器決定撇寞。 |
Dex 加載相關 |
國際化相關
運行在8.0受到影響 | 可能受到影響的功能 |
---|---|
Currency.getDisplayName()顿天、Currency.getSymbol()堂氯、 Locale.getDisplayScript() 默認調(diào)用Locale.getDefault(Category.DISPLAY) |
國際化顯示 |
Currency.getDisplayName(null)將會拋出異常 | 國際化單位顯示 |
對于SimpleDateFormat 的時區(qū)獲取由原來在設備第一次啟動時候獲取,改為每次實時獲取 |
時區(qū)顯示 |
升級ICU到58版本 | 國際化單位標準 |
網(wǎng)絡相關
運行在8.0受到影響 | 可能受到影響的功能 |
---|---|
無正文的 OPTIONS 請求具有 Content-Length: 0 頭部 | options請求相關 |
HttpURLConnection會保證請求最后帶上“/” | HttpURLConnection |
ProxySelector.setDefault() 設置的代理僅處理scheme/host/port牌废,不會處理請求參數(shù) |
代理設置相關功能 |
不再支持空lable的URI | 使用URI相關功能 |
HttpsURLConnection不會執(zhí)行不安全的TLS/SSL協(xié)議版本回退 | HttpsURLConnection |
隧道Https協(xié)議改變咽白,具體見Networking and HTTP(S) connectivity | 隧道Https |
如果DatagramSocket.connect() 返回錯誤,DatagramSocket.send()也會返回錯誤 |
socket相關 |
InetAddress.isReachable() 會在會退到TCP Echo協(xié)議之前嘗試ICMP協(xié)議鸟缕,若不可達會消耗更多時間 |
IP地址判斷是否可達等網(wǎng)絡功能 |
在支持設備上wifi連接當有強度大且已經(jīng)保存的網(wǎng)絡時可以自動切換 | 需保證網(wǎng)絡切換不會影響應用功能 |
界面相關
運行在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 |
網(wǎng)頁相關 |
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受到影響 | 可能受到影響的功能 |
---|---|
音頻獲取焦點時會自動降低其他音頻音量,現(xiàn)在支持暫停而不是降低音量追迟,詳見automatic ducking | 音頻播放相關功能 |
當來電時溶其,自動靜音音頻播放 | 音頻播放相關功能 |
需要使用AudioAttributes實現(xiàn)音頻回放功能,AudioTrack過期 | 音頻回放功能 |
音量按鍵事件會優(yōu)先給前臺activity敦间,如果前臺activity不處理會給最近一次播放音頻的應用 | 音量控制 |
其他
運行在8.0受到影響 | 可能受到影響的功能 |
---|---|
應用快捷方式不能通過com.android.launcher.action.INSTALL_SHORTCUT 創(chuàng)建瓶逃,需要使用ShortcutManager,具體如何創(chuàng)建可以看這篇文章 |
快捷方式創(chuàng)建功能 |
無障礙功能中雙擊動作轉換為點擊動作廓块、 能識別TextView中的ClickableSpan |
無障礙功能 |
findViewById() 返回類型由View改為<T extends View> T
|
覆蓋findViewById() 的地方需要相應修改 |
從2019年1月7日起厢绝,將無法通過 LAST_TIME_CONTACTED /TIMES_CONTACTED /LAST_TIME_USED /TIMES_USED 獲取聯(lián)系人使用情況 |
聯(lián)系人聯(lián)系情況獲取功能 |
AbstractCollection.removeAll(java.util.Collection) /AbstractCollection.retainAll(java.util.Collection) 當傳入?yún)?shù)為null時會報 NullPointerException
|
集合操作 |
Target在8.0受到影響 | 可能受到影響的功能 |
---|---|
瀏覽器ua會包含OPR 有可能導致判斷是否Opera瀏覽器失效 |
根據(jù)ua判斷瀏覽器 |
Collections.sort()改為在List.sort()基礎上實現(xiàn),之前是恰好相反带猴。 如果在List.sort()中調(diào)用Collections.sort()會產(chǎn)生死循環(huán) |
集合排序 |
在遍歷的過程中進行排序昔汉,現(xiàn)在使用無論使用List.sort() 還是Collections.sort() 都會報錯 |
集合排序 |