前言
Google Play應(yīng)用市場(chǎng)對(duì)于應(yīng)用的targetSdkVersion有了更為嚴(yán)格的要求凿叠。從 2018 年 8 月 1 日起戈抄,所有向 Google Play 首次提交的新應(yīng)用都必須針對(duì) Android 8.0 (API 等級(jí) 26) 開(kāi)發(fā)族阅; 2018 年 11 月 1 日起认臊,所有 Google Play 的現(xiàn)有應(yīng)用更新同樣必須針對(duì) Android 8.0扳炬。轉(zhuǎn)載請(qǐng)注明來(lái)源「Bug總柴」
以下記錄了我們升級(jí)targetSdkVersion的坑以及解決辦法之碗,希望對(duì)各位開(kāi)發(fā)者有幫助蝙眶。
錯(cuò)誤1. java.lang.IllegalStateException: Not allowed to start service Intent {}: app is in background uid UidRecord{}
原因分析
從Android8.0開(kāi)始,系統(tǒng)會(huì)對(duì)后臺(tái)執(zhí)行進(jìn)行限制褪那。初步判斷由于我們應(yīng)用在Application的onCreate過(guò)程中使用了IntentService來(lái)后臺(tái)初始化一些任務(wù)幽纷,這個(gè)時(shí)候被系統(tǒng)認(rèn)為是應(yīng)用還處于后臺(tái),從而報(bào)出了java.lang.IllegalStateException錯(cuò)誤博敬。
解決辦法
解決后臺(tái)服務(wù)的限制友浸,首先想到的辦法是將服務(wù)變成前臺(tái)服,隨即我們又遇到了另一個(gè)問(wèn)題偏窝,見(jiàn)錯(cuò)誤2
錯(cuò)誤2. android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{}
原因分析
見(jiàn)Android8.0行為變更收恢。新的 Context.startForegroundService() 函數(shù)將啟動(dòng)一個(gè)前臺(tái)服務(wù)。現(xiàn)在祭往,即使應(yīng)用在后臺(tái)運(yùn)行伦意,系統(tǒng)也允許其調(diào)用 Context.startForegroundService()。不過(guò)硼补,應(yīng)用必須在創(chuàng)建服務(wù)后的五秒內(nèi)調(diào)用該服務(wù)的 startForeground() 函數(shù)驮肉。
解決辦法
在后臺(tái)服務(wù)啟動(dòng)執(zhí)行執(zhí)行之后,通過(guò)Service.startForeground()方法傳入notification變成前臺(tái)服務(wù)已骇。需要注意的是從Android8.0開(kāi)始离钝,Notification必須制定Channel才可以正常彈出通知票编,如果創(chuàng)建Notification Channels詳見(jiàn)這里。
由于我們的初衷是在啟動(dòng)程序的過(guò)程中后臺(tái)進(jìn)行一些初始化奈辰,這種前臺(tái)給用戶帶來(lái)感知的效果并不是我們所希望的,因此我們考慮可以采用另一個(gè)后臺(tái)執(zhí)行任務(wù)的方法乱豆。這里官方推薦使用JobScheduler奖恰。由于我們引入了ktx以及WorkManager,這里我們采用了OneTimeWorkRequest來(lái)實(shí)現(xiàn)宛裕。具體實(shí)現(xiàn)如下:
class InitWorker : Worker(){
override fun doWork(): Result {
// 把耗時(shí)的啟動(dòng)任務(wù)放在這里
return Result.SUCCESS
}
}
然后在Applicaiton的onCreate中調(diào)用
val initWork = OneTimeWorkRequestBuilder<InitWorker>().build()
WorkManager.getInstance().enqueue(initWork)
來(lái)執(zhí)行后臺(tái)初始化工作
錯(cuò)誤3. java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/ProtocolVersion; Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.http.ProtocolVersion"
原因分析
Android P Developer Preview的bug
解決辦法
在AndroidManifest.xml文件中<Application>標(biāo)簽里面加入
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
錯(cuò)誤4. java.io.IOException: Cleartext HTTP traffic to dict.youdao.com not permitted
原因分析
從Android 6.0開(kāi)始引入了對(duì)Https的推薦支持瑟啃,與以往不同,Android P的系統(tǒng)上面默認(rèn)所有Http的請(qǐng)求都被阻止了揩尸。
<application android:usesCleartextTraffic=["true" | "false"]>
原本這個(gè)屬性的默認(rèn)值從true改變?yōu)閒alse
解決辦法
解決的辦法簡(jiǎn)單來(lái)說(shuō)可以通過(guò)在AnroidManifest.xml中的application顯示設(shè)置
<application android:usesCleartextTraffic="true">
更為根本的解決辦法是修改應(yīng)用程序中Http的請(qǐng)求為Https蛹屿,當(dāng)然這也需要服務(wù)端的支持。
錯(cuò)誤5. android.os.FileUriExposedException file exposed beyond app through Intent.getData()
原因分析
主要原因是7.0系統(tǒng)對(duì)file uri的暴露做了限制岩榆,加強(qiáng)了安全機(jī)制错负。詳見(jiàn):官方文檔
代碼里出現(xiàn)問(wèn)題的原因是,在需要安裝應(yīng)用的時(shí)候?qū)⑾螺d下來(lái)的安裝包地址傳給了application/vnd.android.package-archive的intent
解決辦法
使用FileProvider
具體代碼可參考這篇文章
簡(jiǎn)單說(shuō)明就是要在AndroidManifest里面聲明FileProvider勇边,并且在xml中聲明需要使用的uri路徑
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
對(duì)應(yīng)的xml/file_paths中指定需要使用的目錄
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="download"
path="yddownload"/>
</paths>
錯(cuò)誤6. java.lang.SecurityException: Failed to find provider ** for user 0; expected to find a valid ContentProvider for this authority
原因分析
target到android8.0之后對(duì)ContentResolver.notifyChange() 以及 registerContentObserver(Uri, boolean, ContentObserver)做了限制犹撒,官方解釋在這里
解決辦法
參考文章
簡(jiǎn)單來(lái)說(shuō)解決的辦法就是創(chuàng)建一個(gè)contentprovider,并在AndroidManifest里面注冊(cè)的provider的authority聲明為registerContentObserver中uri的authority就可以了粒褒。
<provider
android:name=".download.DownloadUriProvider"
android:authorities="${applicationId}"
android:enabled="true"
android:exported="false"/>
public class DownloadUriProvider extends ContentProvider {
public DownloadUriProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
}
錯(cuò)誤7. notification沒(méi)有顯示
原因分析
如果targetsdkversion設(shè)定為26或以上识颊,開(kāi)始要求notification必須知道channel,具體查閱這里奕坟。
解決辦法
在notify之前先創(chuàng)建notificationChannel
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "下載提醒";
String description = "顯示下載過(guò)程及進(jìn)度";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(DOWNLOAD_CHANNEL_ID, name, importance);
channel.setDescription(description);
mNotificationManager.createNotificationChannel(channel);
}
}
錯(cuò)誤8. 在AndroidManifest中注冊(cè)的receiver不能收到廣播
原因分析
針對(duì)targetsdkversion為26的應(yīng)用祥款,加強(qiáng)對(duì)匿名receiver的控制,以至于在manifest中注冊(cè)的隱式receiver都失效月杉。具體見(jiàn)官方原文
解決辦法
將廣播從在AndroidManifest中注冊(cè)移到在Activity中使用registerReceiver注冊(cè)
錯(cuò)誤9. 無(wú)法通過(guò)“application/vnd.android.package-archive” action安裝應(yīng)用
原因分析
targetsdkversion大于25必須聲明REQUEST_INSTALL_PACKAGES權(quán)限刃跛,見(jiàn)官方說(shuō)明:
REQUEST_INSTALL_PACKAGES
解決辦法
在AndroidManifest中加入
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
錯(cuò)誤10. java.lang.RuntimeException: Unable to start activity ComponentInfo{xxxActivity}: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
原因分析
targetsdk26以上,對(duì)于透明主題的activity不能夠通過(guò)manifest設(shè)定android:screenOrientation苛萎。
具體分析見(jiàn)這里
解決辦法
檢查報(bào)錯(cuò)的Activity是否在AndroidManifest中聲明了
奠伪,若有需要將其去除。