免费国产精品自在自线-91精品国产色综合久久久浪潮-99热久久免费频精品-国产精品国模在线观看-久久亚洲国产精品成人?V秋霞-久久国产一级A片免费播放-亚洲国产欧洲综合97久久-久久国产白嫩美女呻吟高潮

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線實(shí)戰(zhàn)洞察。

Android應(yīng)用內(nèi)更新全流程:從下載到安裝的兼容性實(shí)現(xiàn)與避坑指南

Android應(yīng)用內(nèi)更新全流程:從下載到安裝的兼容性實(shí)現(xiàn)與避坑指南 1. 項(xiàng)目概述從需求到實(shí)現(xiàn)的完整閉環(huán)在移動(dòng)應(yīng)用開(kāi)發(fā)與維護(hù)的日常工作中有一個(gè)場(chǎng)景幾乎每個(gè)開(kāi)發(fā)者都會(huì)遇到應(yīng)用內(nèi)版本更新。用戶(hù)不可能每次都去應(yīng)用商店手動(dòng)下載新版本一個(gè)流暢、可靠、用戶(hù)無(wú)感的應(yīng)用內(nèi)更新體驗(yàn)是提升產(chǎn)品口碑和用戶(hù)留存的關(guān)鍵。這個(gè)需求的核心就是實(shí)現(xiàn)“Android下載APK并安裝APK”。聽(tīng)起來(lái)簡(jiǎn)單不就是從網(wǎng)絡(luò)拉個(gè)文件然后點(diǎn)一下安裝嗎但如果你真這么想那踩坑之路就在眼前了。從Android 7.0的FileProvider適配到Android 8.0的未知來(lái)源應(yīng)用安裝權(quán)限再到Android 11的Scoped Storage和包可見(jiàn)性以及后臺(tái)下載的穩(wěn)定性、安裝過(guò)程的兼容性每一步都藏著細(xì)節(jié)和“玄學(xué)”。我經(jīng)歷過(guò)因?yàn)镕ileProvider的authorities配置錯(cuò)誤導(dǎo)致安裝閃退也處理過(guò)在部分國(guó)產(chǎn)定制系統(tǒng)上權(quán)限彈窗不彈出的詭異問(wèn)題。所以今天我想拋開(kāi)那些簡(jiǎn)單的Demo代碼從一個(gè)需要實(shí)際上線、面對(duì)海量用戶(hù)和各種奇奇怪怪手機(jī)型號(hào)的視角來(lái)完整拆解這個(gè)功能。我們會(huì)涵蓋從網(wǎng)絡(luò)請(qǐng)求、文件下載、存儲(chǔ)路徑選擇、運(yùn)行時(shí)權(quán)限動(dòng)態(tài)申請(qǐng)、不同Android版本的安裝適配到錯(cuò)誤處理和用戶(hù)體驗(yàn)優(yōu)化的全流程。目標(biāo)不僅是讓你能寫(xiě)出代碼更是讓你理解每一行代碼背后的“為什么”以及當(dāng)出現(xiàn)問(wèn)題的時(shí)候你該如何像老中醫(yī)一樣“望聞問(wèn)切”。2. 核心思路與架構(gòu)設(shè)計(jì)在動(dòng)手寫(xiě)代碼之前我們先得把思路理清楚。一個(gè)健壯的更新功能不能是“一次性”的腳本它應(yīng)該是一個(gè)有狀態(tài)、可管理、善始善終的完整流程。2.1 功能流程拆解整個(gè)流程可以抽象為以下幾個(gè)核心階段它們構(gòu)成了一個(gè)清晰的有限狀態(tài)機(jī)檢查更新向你的服務(wù)器接口請(qǐng)求比對(duì)當(dāng)前版本與最新版本信息。這部分涉及網(wǎng)絡(luò)請(qǐng)求和版本號(hào)解析注意不要簡(jiǎn)單比較字符串要使用VersionCode這類(lèi)整型值。用戶(hù)交互與確認(rèn)彈出更新對(duì)話框展示新版本特性、更新包大小等信息等待用戶(hù)確認(rèn)。這里可以設(shè)計(jì)“強(qiáng)制更新”和“可選更新”兩種模式。下載任務(wù)管理用戶(hù)確認(rèn)后啟動(dòng)下載任務(wù)。這里的關(guān)鍵是后臺(tái)服務(wù)與通知欄進(jìn)度的結(jié)合。用戶(hù)切到后臺(tái)甚至鎖屏下載仍需繼續(xù)并且要讓用戶(hù)能清晰地看到進(jìn)度。文件存儲(chǔ)與校驗(yàn)APK文件下載到設(shè)備的哪個(gè)位置下載完成后如何校驗(yàn)文件的完整性例如比對(duì)MD5或文件大小存儲(chǔ)路徑的選擇直接影響后續(xù)的安裝步驟。安裝觸發(fā)與兼容性處理這是最復(fù)雜的一步。你需要構(gòu)建一個(gè)指向APK文件的Uri然后發(fā)送一個(gè)ACTION_INSTALL_PACKAGE或ACTION_VIEW的Intent。但如何構(gòu)建這個(gè)Uri在Android不同版本上截然不同。安裝結(jié)果回調(diào)與清理安裝完成后無(wú)論是成功還是用戶(hù)取消都應(yīng)該有相應(yīng)的回調(diào)處理。成功則可能引導(dǎo)用戶(hù)打開(kāi)新版本失敗則給出提示。最后別忘了清理臨時(shí)文件。2.2 技術(shù)方案選型圍繞上述流程我們需要做出幾個(gè)關(guān)鍵的技術(shù)選擇網(wǎng)絡(luò)請(qǐng)求庫(kù)OkHttp是絕對(duì)的主流選擇。它穩(wěn)定、高效天然支持進(jìn)度回調(diào)是我們實(shí)現(xiàn)下載進(jìn)度監(jiān)聽(tīng)的基礎(chǔ)。后臺(tái)任務(wù)執(zhí)行雖然WorkManager是官方推薦的持久化后臺(tái)任務(wù)解決方案但對(duì)于文件下載這種需要精確控制如暫停、繼續(xù)且與通知欄強(qiáng)關(guān)聯(lián)的場(chǎng)景我更傾向于使用Service特別是ForegroundService前臺(tái)服務(wù)。從Android 8.0開(kāi)始后臺(tái)服務(wù)限制變多但前臺(tái)服務(wù)可以持續(xù)運(yùn)行并提供常駐通知完美契合下載需求。文件存儲(chǔ)這是適配的重災(zāi)區(qū)。Android 10 (API 29) 以下我們可以使用應(yīng)用私有目錄如getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)。這個(gè)路徑不需要存儲(chǔ)權(quán)限。Android 10 (API 29) 及以上由于Scoped Storage應(yīng)用私有目錄仍然是首選。但如果你希望用戶(hù)能在“文件管理”應(yīng)用中看到這個(gè)APK可能需要使用MediaStoreAPI但這會(huì)引入額外的復(fù)雜性。對(duì)于更新功能我強(qiáng)烈建議只使用應(yīng)用私有目錄這樣最安全、權(quán)限問(wèn)題最少。安裝適配這是版本適配的核心。Android 7.0 (API 24) 及以上禁止使用file://Uri直接傳遞文件必須使用FileProvider生成一個(gè)content://Uri。Android 8.0 (API 26) 及以上安裝未知來(lái)源應(yīng)用需要?jiǎng)討B(tài)申請(qǐng)REQUEST_INSTALL_PACKAGES權(quán)限不再是設(shè)置中的一個(gè)靜態(tài)開(kāi)關(guān)。Android 11 (API 30) 及以上除了上述兩點(diǎn)還引入了包可見(jiàn)性限制。如果你的targetSdkVersion 30默認(rèn)無(wú)法查詢(xún)到其他應(yīng)用的信息也無(wú)法直接安裝其他應(yīng)用除非是應(yīng)用商店。你需要在中聲明queries或者使用PackageInstallerAPI進(jìn)行更底層的安裝。基于以上分析我們的架構(gòu)藍(lán)圖就清晰了使用OkHttp在ForegroundService中執(zhí)行下載將APK保存到應(yīng)用私有目錄下載完成后根據(jù)系統(tǒng)版本動(dòng)態(tài)處理權(quán)限和Uri最后發(fā)送安裝Intent。3. 核心實(shí)現(xiàn)與代碼詳解理論說(shuō)完我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我會(huì)把代碼分成幾個(gè)關(guān)鍵模塊并解釋每一處的設(shè)計(jì)考量。3.1 基礎(chǔ)配置與聲明首先在AndroidManifest.xml中我們需要添加必要的權(quán)限、組件和Provider聲明。!-- 網(wǎng)絡(luò)權(quán)限 -- uses-permission android:nameandroid.permission.INTERNET / !-- 寫(xiě)入外部存儲(chǔ)在Android 10以下如果存到公共目錄可能需要 -- uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE android:maxSdkVersion28 / !-- 僅對(duì)Android 9及以下生效 -- !-- 安裝未知來(lái)源應(yīng)用的權(quán)限Android 8.0 -- uses-permission android:nameandroid.permission.REQUEST_INSTALL_PACKAGES/ !-- 前臺(tái)服務(wù)類(lèi)型聲明Android 9.0 -- uses-permission android:nameandroid.permission.FOREGROUND_SERVICE / !-- 聲明FileProvider -- application ... provider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider !-- 建議使用包名確保唯一性 -- android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths / /provider !-- 聲明我們的下載服務(wù) -- service android:name.service.ApkDownloadService android:enabledtrue android:exportedfalse / /application接下來(lái)創(chuàng)建res/xml/file_paths.xml文件定義FileProvider可以共享的文件路徑。?xml version1.0 encodingutf-8? paths xmlns:androidhttp://schemas.android.com/apk/res/android !-- 對(duì)應(yīng) Context.getExternalFilesDir() 返回的路徑 -- external-files-path nameexternal_files path. / !-- 對(duì)應(yīng) Context.getFilesDir() 返回的路徑 -- files-path nameinternal_files path. / !-- 對(duì)應(yīng) Environment.getExternalStorageDirectory() 返回的路徑謹(jǐn)慎使用 -- external-path nameexternal_storage_root path. / /paths注意authorities屬性必須全局唯一通常使用${applicationId}你的應(yīng)用包名作為前綴后面加上.fileprovider這樣可以有效避免與其他應(yīng)用沖突。grantUriPermissionstrue是必須的它允許我們臨時(shí)授權(quán)給安裝程序訪問(wèn)Uri的權(quán)限。3.2 下載服務(wù)ForegroundService與OkHttp我們創(chuàng)建一個(gè)ApkDownloadService它繼承自Service并在開(kāi)始下載時(shí)啟動(dòng)為前臺(tái)服務(wù)。// 使用Kotlin編寫(xiě)邏輯更清晰 class ApkDownloadService : Service() { private val notificationManager by lazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } private val channelId apk_download_channel private val notificationId 1 private var downloadCall: Call? null // 用于取消下載 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.getStringExtra(EXTRA_DOWNLOAD_URL)?.let { url - startForegroundDownload(url) } // 如果服務(wù)被殺死不自動(dòng)重啟避免重復(fù)下載 return START_NOT_STICKY } private fun startForegroundDownload(downloadUrl: String) { // 1. 創(chuàng)建通知渠道Android 8.0 if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { val channel NotificationChannel( channelId, 應(yīng)用更新, NotificationManager.IMPORTANCE_LOW ).apply { description 下載新版本安裝包 setSound(null, null) // 下載通知通常不需要聲音 lockscreenVisibility Notification.VISIBILITY_PUBLIC } notificationManager.createNotificationChannel(channel) } // 2. 構(gòu)建初始通知 val notification NotificationCompat.Builder(this, channelId) .setContentTitle(正在下載更新) .setContentText(準(zhǔn)備中...) .setSmallIcon(android.R.drawable.stat_sys_download) // 使用系統(tǒng)圖標(biāo) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) // 持續(xù)通知 .setOnlyAlertOnce(true) // 僅首次提醒 .build() // 3. 啟動(dòng)為前臺(tái)服務(wù) startForeground(notificationId, notification) // 4. 開(kāi)始異步下載 downloadApk(downloadUrl) } private fun downloadApk(url: String) { // 確定存儲(chǔ)路徑和文件名 val apkDir getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) // 文件名可以從URL解析或服務(wù)器返回這里簡(jiǎn)單處理 val apkName update_${System.currentTimeMillis()}.apk val apkFile File(apkDir, apkName) val request Request.Builder().url(url).build() val client OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build() downloadCall client.newCall(request) downloadCall?.enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 下載失敗更新通知并停止前臺(tái)服務(wù) updateNotification(下載失敗: ${e.message}, false) stopForeground(true) // 可以通過(guò)廣播等方式通知Activity更新UI sendDownloadFailedBroadcast(e.message) } override fun onResponse(call: Call, response: Response) { if (!response.isSuccessful) { onFailure(call, IOException(服務(wù)器錯(cuò)誤: ${response.code})) return } response.body?.let { body - val totalLength body.contentLength() var downloadedLength: Long 0 val buffer ByteArray(8192) // 8K緩沖區(qū) var inputStream: InputStream? null var outputStream: FileOutputStream? null try { inputStream body.byteStream() outputStream FileOutputStream(apkFile) var read: Int while (inputStream.read(buffer).also { read it } ! -1) { outputStream.write(buffer, 0, read) downloadedLength read.toLong() // 計(jì)算進(jìn)度更新通知避免更新太頻繁 val progress (downloadedLength * 100 / totalLength).toInt() runOnUiThread { // 通知更新需在主線程 updateNotification(下載中 $progress%, true, progress) } } outputStream.flush() // 下載成功 runOnUiThread { updateNotification(下載完成點(diǎn)擊安裝, false) stopForeground(false) // 停止前臺(tái)服務(wù)但保留通知 // 發(fā)送下載成功廣播攜帶文件路徑 sendDownloadSuccessBroadcast(apkFile.absolutePath) } } catch (e: IOException) { apkFile.delete() // 刪除不完整的文件 onFailure(call, e) } finally { inputStream?.close() outputStream?.close() } } } }) } private fun updateNotification(contentText: String, ongoing: Boolean, progress: Int 0) { val builder NotificationCompat.Builder(this, channelId) .setContentTitle(應(yīng)用更新) .setContentText(contentText) .setSmallIcon(android.R.drawable.stat_sys_download) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(ongoing) .setOnlyAlertOnce(true) if (ongoing progress in 0..100) { builder.setProgress(100, progress, false) // 確定性進(jìn)度條 } notificationManager.notify(notificationId, builder.build()) } override fun onDestroy() { downloadCall?.cancel() // 服務(wù)銷(xiāo)毀時(shí)取消網(wǎng)絡(luò)請(qǐng)求 super.onDestroy() } override fun onBind(intent: Intent?): IBinder? null companion object { const val EXTRA_DOWNLOAD_URL extra_download_url // 啟動(dòng)服務(wù)的方法 fun start(context: Context, downloadUrl: String) { val intent Intent(context, ApkDownloadService::class.java).apply { putExtra(EXTRA_DOWNLOAD_URL, downloadUrl) } if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { context.startForegroundService(intent) } else { context.startService(intent) } } } }實(shí)操心得在onStartCommand中返回START_NOT_STICKY非常重要。如果下載服務(wù)因?yàn)橄到y(tǒng)資源緊張被殺死我們不希望它自動(dòng)重啟因?yàn)檫@可能導(dǎo)致重復(fù)下載或狀態(tài)混亂。更好的做法是由前臺(tái)的Activity或一個(gè)持久化的任務(wù)調(diào)度器如WorkManager來(lái)監(jiān)聽(tīng)網(wǎng)絡(luò)狀態(tài)當(dāng)服務(wù)意外停止時(shí)由它們決定是否重新觸發(fā)下載流程。此外進(jìn)度更新不要每次寫(xiě)入都刷新通知可以設(shè)置一個(gè)閾值比如進(jìn)度每增加5%更新一次或者使用Handler進(jìn)行延遲更新以減少性能開(kāi)銷(xiāo)。3.3 安裝適配權(quán)限、Uri與Intent下載完成后我們得到了APK文件的路徑。接下來(lái)就是最關(guān)鍵的安裝步驟。我們需要一個(gè)統(tǒng)一的安裝入口它能處理所有Android版本的差異。object ApkInstaller { /** * 安裝APK文件 * param context Context * param apkFilePath APK文件的絕對(duì)路徑 */ fun installApk(context: Context, apkFilePath: String) { val apkFile File(apkFilePath) if (!apkFile.exists()) { Toast.makeText(context, 安裝文件不存在, Toast.LENGTH_SHORT).show() return } // 步驟1: 檢查并申請(qǐng)安裝未知來(lái)源權(quán)限 (Android 8.0) if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { if (!context.packageManager.canRequestPackageInstalls()) { // 沒(méi)有權(quán)限跳轉(zhuǎn)設(shè)置頁(yè)面申請(qǐng) val intent Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply { data Uri.parse(package:${context.packageName}) } // 這里需要在一個(gè)Activity中啟動(dòng)并處理onActivityResult // 通常我們會(huì)先彈窗提示用戶(hù)然后在用戶(hù)確認(rèn)后啟動(dòng)這個(gè)Intent // 為了示例清晰這里假設(shè)調(diào)用者是一個(gè)Activity并會(huì)處理權(quán)限回調(diào) (context as? Activity)?.startActivityForResult(intent, REQUEST_CODE_INSTALL_PERMISSION) return // 等待權(quán)限申請(qǐng)結(jié)果 } } // 步驟2: 獲取APK文件的Uri (適配Android 7.0) val apkUri getApkUri(context, apkFile) // 步驟3: 構(gòu)建安裝Intent并啟動(dòng) val installIntent Intent(Intent.ACTION_VIEW).apply { setDataAndType(apkUri, application/vnd.android.package-archive) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予臨時(shí)讀取權(quán)限 // Android 11 可能需要添加此Flag以允許安裝完成后返回原應(yīng)用 if (Build.VERSION.SDK_INT Build.VERSION_CODES.R) { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } } // 步驟4: 為目標(biāo)Intent選擇Activity (處理包可見(jiàn)性Android 11) val resolveInfoList context.packageManager.queryIntentActivities( installIntent, if (Build.VERSION.SDK_INT Build.VERSION_CODES.R) { PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()) } else { PackageManager.MATCH_DEFAULT_ONLY } ) if (resolveInfoList.isEmpty()) { // 沒(méi)有應(yīng)用能處理安裝Intent這幾乎不可能發(fā)生除非系統(tǒng)被嚴(yán)重修改 Toast.makeText(context, 無(wú)法找到安裝程序, Toast.LENGTH_SHORT).show() return } // 步驟5: 啟動(dòng)安裝頁(yè)面 context.startActivity(installIntent) } /** * 根據(jù)Android版本獲取正確的Uri */ private fun getApkUri(context: Context, apkFile: File): Uri { return if (Build.VERSION.SDK_INT Build.VERSION_CODES.N) { // Android 7.0 使用FileProvider FileProvider.getUriForFile( context, ${context.packageName}.fileprovider, // 必須與manifest中聲明的authorities一致 apkFile ) } else { // Android 7.0以下使用傳統(tǒng)的file:// Uri Uri.fromFile(apkFile) } } const val REQUEST_CODE_INSTALL_PERMISSION 10001 }在調(diào)用installApk的Activity中你需要處理權(quán)限申請(qǐng)的回調(diào)class MainActivity : AppCompatActivity() { ... private var pendingApkPath: String? null // 保存等待安裝的文件路徑 fun onDownloadComplete(apkPath: String) { pendingApkPath apkPath ApkInstaller.installApk(this, apkPath) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode ApkInstaller.REQUEST_CODE_INSTALL_PERMISSION) { // 用戶(hù)從設(shè)置頁(yè)面返回再次檢查權(quán)限 if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { if (packageManager.canRequestPackageInstalls()) { // 用戶(hù)已授權(quán)繼續(xù)安裝 pendingApkPath?.let { ApkInstaller.installApk(this, it) } } else { Toast.makeText(this, 需要授權(quán)才能安裝應(yīng)用, Toast.LENGTH_SHORT).show() } } pendingApkPath null } } }注意事項(xiàng)FileProvider.getUriForFile的第二個(gè)參數(shù)authorities必須與AndroidManifest.xml中provider標(biāo)簽下定義的android:authorities屬性完全一致包括大小寫(xiě)。這是最常見(jiàn)的錯(cuò)誤來(lái)源之一會(huì)導(dǎo)致安裝時(shí)出現(xiàn)“解析包出錯(cuò)”或直接閃退。另外在Android 11上即使你正確申請(qǐng)了REQUEST_INSTALL_PACKAGES權(quán)限如果targetSdkVersion 30默認(rèn)也無(wú)法通過(guò)queryIntentActivities查到系統(tǒng)安裝器。你需要在AndroidManifest.xml的queries標(biāo)簽內(nèi)聲明intent或者使用PackageInstallerAPI。對(duì)于大多數(shù)應(yīng)用聲明queries更簡(jiǎn)單manifest ... queries !-- 為了啟動(dòng)系統(tǒng)安裝界面 -- intent action android:nameandroid.intent.action.INSTALL_PACKAGE / data android:mimeTypeapplication/vnd.android.package-archive / /intent !-- 也可以聲明VIEW intent更通用 -- intent action android:nameandroid.intent.action.VIEW / category android:nameandroid.intent.category.DEFAULT / data android:mimeTypeapplication/vnd.android.package-archive / /intent /queries ... /manifest3.4 整合與UI交互現(xiàn)在我們將檢查更新、下載、安裝串聯(lián)起來(lái)并設(shè)計(jì)一個(gè)友好的用戶(hù)界面。1. 檢查更新通常你有一個(gè)服務(wù)器接口返回類(lèi)似這樣的JSON{ latestVersionCode: 20240501, latestVersionName: 2.1.0, downloadUrl: https://your-cdn.com/app/update_2.1.0.apk, updateLog: 1. 修復(fù)了已知問(wèn)題\n2. 提升了性能, forceUpdate: false, fileSize: 50485760 // 單位字節(jié) }在客戶(hù)端解析后與當(dāng)前版本BuildConfig.VERSION_CODE比較決定是否彈出更新對(duì)話框。2. 更新對(duì)話框使用AlertDialog或自定義Dialog展示版本信息、更新日志和文件大小。提供“立即更新”和“稍后再說(shuō)”按鈕如果是強(qiáng)制更新則只保留“立即更新”并不可取消。3. 啟動(dòng)下載用戶(hù)點(diǎn)擊“立即更新”后首先檢查必要的運(yùn)行時(shí)權(quán)限如Android 13的POST_NOTIFICATIONS通知權(quán)限然后啟動(dòng)我們的ApkDownloadService。// 在Activity或ViewModel中 fun startDownload(downloadUrl: String) { // 檢查通知權(quán)限 (Android 13) if (Build.VERSION.SDK_INT Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) ! PackageManager.PERMISSION_GRANTED) { // 請(qǐng)求權(quán)限在回調(diào)中再啟動(dòng)服務(wù) requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), REQUEST_CODE_NOTIFICATION) return } } // 啟動(dòng)下載服務(wù) ApkDownloadService.start(context, downloadUrl) // 可以在這里顯示一個(gè)進(jìn)度條在UI上與服務(wù)通過(guò)LiveData或廣播通信 }4. 監(jiān)聽(tīng)下載進(jìn)度與完成ApkDownloadService通過(guò)LocalBroadcastManager或更好的方式如LiveData在ViewModel中將下載進(jìn)度和完成事件通知給UI。UI層更新進(jìn)度條并在下載完成后調(diào)用ApkInstaller.installApk。4. 避坑指南與進(jìn)階優(yōu)化實(shí)現(xiàn)基本功能后我們來(lái)看看那些容易踩坑的地方和一些優(yōu)化技巧。4.1 常見(jiàn)問(wèn)題排查表問(wèn)題現(xiàn)象可能原因解決方案點(diǎn)擊安裝沒(méi)反應(yīng)1. Uri構(gòu)建錯(cuò)誤FileProvider配置問(wèn)題。2. Android 8.0未動(dòng)態(tài)申請(qǐng)REQUEST_INSTALL_PACKAGES權(quán)限。3. Android 11包可見(jiàn)性問(wèn)題。1. 檢查file_paths.xml和authorities是否匹配使用adb logcat查看相關(guān)錯(cuò)誤。2. 在安裝前調(diào)用PackageManager.canRequestPackageInstalls()檢查并引導(dǎo)用戶(hù)授權(quán)。3. 在AndroidManifest.xml中添加queries聲明。安裝時(shí)提示“解析包時(shí)出現(xiàn)問(wèn)題”1. APK文件下載不完整或損壞。2. APK文件與當(dāng)前CPU架構(gòu)不兼容如x86設(shè)備安裝了arm包。3. 簽名不一致覆蓋安裝時(shí)。1. 下載完成后校驗(yàn)文件MD5或大小。2. 服務(wù)器應(yīng)提供適配不同ABI的APK或提供通用包universal APK。3. 確保用于更新的APK簽名與已安裝版本一致。通知欄不顯示下載進(jìn)度1. Android 8.0未創(chuàng)建通知渠道。2. 前臺(tái)服務(wù)未正確啟動(dòng)startForeground沒(méi)調(diào)用或調(diào)用太晚。3. 通知被用戶(hù)關(guān)閉或系統(tǒng)限制。1. 確保在startForeground前創(chuàng)建NotificationChannel。2. 在onStartCommand中盡快調(diào)用startForeground。3. 檢查應(yīng)用的通知權(quán)限是否被關(guān)閉。下載到一半中斷無(wú)法續(xù)傳使用OkHttp的普通Call不支持?jǐn)帱c(diǎn)續(xù)傳。使用OkHttp的Interceptor記錄已下載大小并在請(qǐng)求頭中添加Range: bytes已下載大小-?;蛘呤褂酶鼘?zhuān)業(yè)的下載庫(kù)如aria2c的封裝或FileDownloader。在部分國(guó)產(chǎn)ROM如MIUI、EMUI上安裝失敗廠商定制系統(tǒng)增加了額外的權(quán)限管理或后臺(tái)限制。1. 引導(dǎo)用戶(hù)將應(yīng)用加入“自啟動(dòng)”和“后臺(tái)運(yùn)行”白名單。2. 嘗試使用PackageInstallerAPI直接安裝繞過(guò)系統(tǒng)安裝界面需要系統(tǒng)權(quán)限或特殊適配不通用。4.2 進(jìn)階優(yōu)化技巧文件校驗(yàn)與安全服務(wù)器在提供下載鏈接時(shí)同時(shí)提供APK文件的MD5或SHA256值。下載完成后在本地計(jì)算文件的哈希值進(jìn)行比對(duì)確保文件未被篡改或損壞。fun verifyFile(file: File, expectedMd5: String): Boolean { val digest MessageDigest.getInstance(MD5) file.inputStream().use { input - val buffer ByteArray(8192) var read: Int while (input.read(buffer).also { read it } ! -1) { digest.update(buffer, 0, read) } } val actualMd5 digest.digest().joinToString() { %02x.format(it) } return actualMd5.equals(expectedMd5, ignoreCase true) }智能重試與網(wǎng)絡(luò)環(huán)境判斷下載失敗后不要立即無(wú)腦重試。可以設(shè)計(jì)一個(gè)指數(shù)退避的重試機(jī)制。在開(kāi)始下載前判斷網(wǎng)絡(luò)環(huán)境。如果是移動(dòng)數(shù)據(jù)且APK文件很大可以提示用戶(hù)是否繼續(xù)或者設(shè)置為僅在Wi-Fi下自動(dòng)下載。使用DownloadManager備選方案對(duì)于不需要精細(xì)控制下載過(guò)程如暫停、續(xù)傳的場(chǎng)景可以考慮使用系統(tǒng)自帶的DownloadManager。它自動(dòng)處理通知、網(wǎng)絡(luò)切換和重試但缺點(diǎn)是回調(diào)不夠靈活且在部分定制系統(tǒng)上行為不一致。安裝完成后的回調(diào)發(fā)送安裝Intent后我們無(wú)法直接知道用戶(hù)是安裝了還是取消了。一個(gè)變通的方法是在發(fā)送Intent前記錄當(dāng)前版本號(hào)稍后例如應(yīng)用回到前臺(tái)時(shí)再次檢查版本號(hào)。如果版本號(hào)變了說(shuō)明安裝成功可以提示用戶(hù)新特性。清理舊安裝包每次更新都會(huì)產(chǎn)生新的APK文件。可以在成功安裝后或應(yīng)用啟動(dòng)時(shí)掃描下載目錄刪除除最新安裝包外的所有歷史APK文件避免占用用戶(hù)存儲(chǔ)空間。4.3 針對(duì)特定場(chǎng)景的補(bǔ)充系統(tǒng)應(yīng)用或Root環(huán)境如果你開(kāi)發(fā)的是系統(tǒng)應(yīng)用或擁有root權(quán)限安裝過(guò)程會(huì)簡(jiǎn)單很多可以直接使用pm install命令無(wú)需處理權(quán)限和FileProvider。但這屬于特殊場(chǎng)景不適用于普通應(yīng)用。插件化或熱更新本文討論的是完整的APK安裝更新。如果是插件化框架需要將APK下載后通過(guò)DexClassLoader等機(jī)制動(dòng)態(tài)加載安裝步驟完全不同。Google Play發(fā)布如果應(yīng)用上架了Google Play強(qiáng)烈建議使用其內(nèi)置的In-app updatesAPI它提供了更原生、更流暢的更新體驗(yàn)包括靈活更新和即時(shí)更新兩種模式能繞過(guò)未知來(lái)源安裝的限制。5. 總結(jié)與個(gè)人體會(huì)實(shí)現(xiàn)一個(gè)健壯的Android應(yīng)用內(nèi)更新功能就像在錯(cuò)綜復(fù)雜的迷宮中鋪設(shè)一條穩(wěn)定可靠的軌道。它不僅僅是“下載安裝”兩個(gè)動(dòng)作的拼接而是網(wǎng)絡(luò)、存儲(chǔ)、權(quán)限、系統(tǒng)版本、用戶(hù)交互和異常處理等多個(gè)領(lǐng)域的交集。從我自己的踩坑經(jīng)驗(yàn)來(lái)看兼容性永遠(yuǎn)是第一位的。你永遠(yuǎn)不知道用戶(hù)手里拿著的是哪個(gè)品牌、哪個(gè)版本、經(jīng)過(guò)怎樣魔改的系統(tǒng)。因此代碼不能只在自己和測(cè)試人員的手機(jī)上跑通就完事必須充分考慮邊界情況低存儲(chǔ)空間怎么辦下載中途來(lái)電話斷網(wǎng)怎么辦用戶(hù)拒絕安裝權(quán)限后又反悔怎么辦其次用戶(hù)體驗(yàn)的細(xì)節(jié)決定成敗。一個(gè)帶清晰進(jìn)度條的通知一個(gè)下載完成后自動(dòng)彈出的安裝提示一句對(duì)文件大小的貼心說(shuō)明都能讓用戶(hù)感受到產(chǎn)品的用心。相反如果下載過(guò)程毫無(wú)反饋安裝時(shí)又跳出一堆令人困惑的系統(tǒng)彈窗更新率必然會(huì)大打折扣。最后安全與穩(wěn)定是底線。校驗(yàn)文件完整性可以防止傳輸錯(cuò)誤或惡意劫持。使用FileProvider和正確的權(quán)限申請(qǐng)流程是遵循Android安全規(guī)范的基本要求。后臺(tái)下載服務(wù)的保活與資源回收也需要仔細(xì)權(quán)衡不能為了下載而過(guò)度消耗電量或流量。把這份代碼和思路應(yīng)用到你的項(xiàng)目中時(shí)建議你先在一個(gè)簡(jiǎn)單的Demo里跑通整個(gè)流程然后逐步將它集成到你的主應(yīng)用里并加上完善的日志記錄。這樣當(dāng)線上用戶(hù)反饋更新失敗時(shí)你才能快速定位問(wèn)題所在。記住沒(méi)有一勞永逸的代碼只有對(duì)細(xì)節(jié)的不斷打磨和對(duì)不同設(shè)備環(huán)境的持續(xù)敬畏才能做出讓用戶(hù)滿意的更新體驗(yàn)。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
婷婷5月天激情综合| 琪琪色综合网站| 97AV在线视频| 中文字幕人妻AV| www.婷婷五月天.com| 亚洲综合新99视频| 99热最新| 久久狠狠干| 蜜桃人妻无码AV天堂三区| 欧美色五月天| 五月丁香六月婷婷亚洲| 丁香六月视频| 久久成人性爱| 99免费成人网| www超碰| av网址在线播放| 天天日天天舔| 婷婷五月av| 亚洲色婷婷激情| 日本九九视频| 婷婷综合一二三| 99ER热精品视频| 九月丁香亭亭| 五月婷婷三级| 香蕉视频91| 六月久久狠狠| 夜夜骑天天操| 极品另类| 丁香六月综合激情| 91丨九色丨丰满人妖| 亚洲日本韩国| 操操操B| 激情五月综合网| 常久最新免费的色吊丝| 久久九九热视频| 婷婷亚洲丁香五月| 久久深爱激情网| 99re热精品视频国| 天天天天天操| 五月婷婷综合潮喷| 一级黄色尤物综合视频手机在线观看| 日韩色五月| 丁香五月激情婷婷激情| 五月天堂在线| 精品人妻一区| 色婷婷香蕉| 26uuu精品一区二区| 色婷婷激情五月天在线观看| www天天色天天射| 激情婷婷综合五月少妇| 五月婷婷啪啪| 综合激情五月丁香9999久久精| 久久九九玖玖| 婷婷五月成人色综合| 丁香五月婷婷久久综合激情网 | 9色91视频| 人妻啪啪啪| 婷婷色情网| 五月天激情国产综合婷婷| 色婷婷六月丁香综合欲精品| 五月婷婷中文字幕| 大伊香蕉精品视频在线| 超碰在线免费| 97在线精品| 99热成人| 超爽内射| a色色色色色| www.99在线| 热的五码久久精品| 日日舔夜夜操| 开心五月网| 丰满女老板BD高清A片| 香蕉AV777XXX色综合一区 | 噜噜噜精品欧美成人在线观看| www.minyis.com【JT】国内CDN落地页保证转化QQ2101460746 | 五月天另类综合网| 色色网站在线| 五月婷婷无码| 日本三级第一页| 久久9热综合| 丁香五月婷婷欧美激情-中文天堂最新版在线观看| 色欧美日| 深爱激情五月天色婷婷| 五月丁香六月婷婷色日| 五月婷婷在线免费观看| 丁香久久激情俄| 久久超级碰碰| 欧美另类五月激情| 日韩色五月| 1024人妻| 91九色成人原创视频| 色婷婷69| 婷婷丁香五月综合网上| 亚洲九九夜夜| 亚洲V国产V欧美V久久久久久| 日本在线观看91| 色婷婷亚洲在线观看| 亚州操操| 桃色激情网| 日韩成人av在线| 无码激情AAAAA片-区区| 久久综合性| 思思热在线精品视频网站| 婷婷色五月综合| 97人凄人人操人人爽| 拍真实国产伦偷精品| 九97免费视频| 国产色色小草视频| 免费日韩99| 5月丁香六月婷婷| 日本久久人人| 婷婷综合日本| 五月丁香啪啪综合| 久久久久久久久月丁| 91婷婷丁香五月| 欧美五月丁香在线观看| 1024日韩| 色性综合| 日韩啪啪网| 天堂久久久久天堂网| 激情五月天视频| 夜夜撸天天日| 成人国产欧美大片一区| 丁香五月瑟瑟| 色情五月天se| 九九色色| 六月99天天婷婷激情综合| 人人噜天天上| 97偷拍在线视频| ...婷婷国产成人亚洲日韩| 激情五月天影院| 国产精品人人做人人爽人人添 | 日日噜狠狠色综合久久| 国产成人精品亚洲线观看| 狠狠色噜噜狠狠| 超级碰碰碰久久网站视频| 亚洲精品色色| 天天干天天日天天操| 综合久久综合五月天婷婷| 九九综合久久丁香婷婷,开心激情综合网| 丁香五月欧美午夜视频| 射琪琪| 97色色色色色色色色色色色色色| 亚洲日本韩国| 大地资源色婷婷视频在线| 丁香香蕉射射射| 蜜桃婷婷五月| 国产精品久久久久久久久久免费| 婷婷五月天激情小说网站| 五月丁香婷婷综合| 亚洲开心激情网| 色婷婷五月天在线观看| 激情五月综合色婷婷| 人人摸人人干| 久久成人综合五月天| 激情五月综合| 日日日影院| 亚洲熟妇AV乱码在线观看| 亚洲xx在线| 丁香五月天色综合| A片试看120分钟做受图片| 91精品熟女| 色色无码| 99热99热99热99热| 精品99在线| 日韩 中文 欧美| 91久久电影| 激情网五月天| 日韩欧美五月丁综合| 五月激情婷婷丁香| 五月天婷婷黄色| 97人人操人人插| 《蜘蛛女》梁铮1995| 九九美女视频| 六月婷婷五月天| 五月天婷婷色色| 婷婷丁香六月| 天天综合社区| 色色五月天婷婷| 久久久网站| 久久er+| 天天操天天爱天天玩| 91人妻九色大屁股| 五月丁香成人| wwwC0maV五月花| 亚洲AV日韩AV永久无码网站| 99色免费观看全部| 五月丁香激情婷婷综合| 日韩性视频| 激情99| A色色| av电影在线播放| 婷婷色色丁香五月天| 91色婷婷综合久久中文字幕二区| 亚洲激情四谢| 另类视频五月天| 五月丁香网中文字幕| 超碰成人在线免费观看| 日本人妻A片成人免费看片| 丁香五月激情婷婷| 伊人大香五月天| 香蕉曰比| 亚洲精品一区中文字幕乱码| 久久激情五月| 五月六月伦理| 久久只这里有精品| 五月天婷五月天综合网小说首页-五月天激激婷婷大综合,婷婷亚洲综合五月天小说 | 狠狠操.COM| 色婷婷先锋| 少妇性按摩无码中文A片| 婷婷成人视频| 天堂AV在线看| 99热热九九| 91综合视频在线| 久久丁香五月| 成人在线综合| 噜噜久| 五月婷婷黄色| 亚洲一区国产传媒| 色婷婷精品视频| 婷婷五月天社区| 这里有精品| 丁香婷婷六月天| 996er热| 日韩三十六页| 国产AV影片| 成人丁香五月| 五月丁香婷婷啪啪| 婷婷五月亚洲综合| 九九色影视| 五月丁香婷婷激情澎湃四射| 久久草婷婷丁香网站| 日日干天天射| 91精品在线看| 日本精品99网站| 九月色婷婷综合亚洲| 成人在线日韩| 成人做爰高潮A片免费视频| 啪啪激情综合| 99re在线观看| 美日韩成人| 五月丁香色综合| 狠狠色丁香久久婷婷综合五月| aⅤ79成人片| 久久精品综合色| av在线不卡播放| 在线五月婷| 激情玖玖sh| 内射爽无广熟女亚洲| 操人91| 五月婷婷激情日本| 婷婷综合亚洲| 99热婷婷| 99久久99久久综合| 欧美三9久九观看| 婷婷亚洲综合| 99久久超级| 国产精产国品一二三在观看| 久草五月丁香婷婷综合| 成人色图情色成人网 www.5b5b5bcom 五月天 | 国产一区二区三区影院| 丁香五月激情图片婷婷| 丁香五月激情欧美| 五月丁香六月综合基地| 日本久久网| 亚洲AV免费在线| 啪啪激情网| 色综合激情| 91丨九色丨东北熟女| 丁香婷婷五月天激情四射| 五月天丁香婷| 天堂爱爱| 久久机热/这里只有精品| 99久久久| 99在线热| 久久综合久色欧美综合狠狠| 久久一操| 啪啪啪综合网| 丁香五月天BBw| 婷婷五月天干干| 五月丁香六月综合激情| 99热精品在线在线| WWW.五月com| 激情啪啪五月天| 99久操| 超碰人妻在线| 深爱五月日韩| 激情小说视频图片| 日韩五月婷婷久久| 五月香婷婷| 在线综合婷婷| 殴美激情综合网| 婷婷激情图片| 五月综合丁| www色婷婷| 99re这里只有精品视频6| 国产高清精品色| 五月丁香色婷婷色| 久久R激情| 久久综合色五月| 久久这里只有精彩| 色五月人妻| 99性视频| 91猫咪国产在线播放| 欧美久久婷婷| 狠狠色综合精品视频在线| 婷婷色九月| 色色色色色色色色色影院| 999久久久国产精品| 婷婷五月丁香基地在线视频官网| 毛片色五月| 91婷婷| 沈娜娜av| 久久五月激情网| 亚洲综合另类| 99精品久久久久久久久| 激情五月婷婷欧美极品| 五月停停色色丁香| 婷婷五月精品中文| 欧美英丁香开心快乐六月天网| 五月丁香激情婷婷| 五月丁了香蕉综合| 这里只有精品在线免费视频| 中文字幕在线日亚州9| 五月天丁香欧美激情| site:minyis.com| 五月激情综合网| 丁香婷婷老熟女综合网| WWW.桔色成人.COM入口| 女人天堂AV| 热久久色| 五月天婷婷小说| 亚洲成人影视在线| 婷婷丁香五月久久| 99热国产| 丁香五月天色| 亚洲人人操| 国产操B视频| 人人爱人人添| 在线不卡的视频| 五月开心六月婷婷在线播放网站| 亚洲激情另类| 激情综合青草| 国产激情久久久| 夜夜 操无码| 99综合| 影音先锋五月婷婷| 天天综合亚洲综合| 97色精品视频| 婷婷五月天日本无码| 疯狂做受XXXX高潮A片动画| 五月丁香六月综合情在线观看 | 九九激情网| 日本久久人| 亚洲视频在线网| 色天天综合成人网| 五月激情久久| 色五月婷婷亚洲最大| ztEJj| 丁香婷婷啪啪啪| 一本大道嫩草AV无码专区| 婷婷五月激情网站| 性一交一乱一交A片久| 久久永久网址| 精品9197碰| 婷婷激情蜜桃玖玖丁香| 超碰国产一区| 丁香六月婷婷久久综合| 久久新地址| 97精品欧美91久久久久久久| Av狠狠色丁香婷| 激情五月婷婷| 一本伊人色婷| WWW·色色色·COM| 精品人妻伦一二三区久久| a色色色色色| 婷婷综合视频| 丁香六月婷婷综合在线| 五月天丁香婷婷社区| 91人操| 婷婷色网站| 99熟女| yazhou seshipin| 成人做爰A片免费看视频| 婷婷色五月久久| 亚洲中文字幕在线电影| 超碰不卡在线| 色五月xxx| 午夜天堂一区人妻| 激情五月天影院| 丁香色色色| 99国产精品久久久久久久久久久| 99性爱视频网站| 婷婷五月天中文字幕| 日韩999| 热无码A∨| av一级棒av| 可以看的AV| 久99久视频| 天天射影院| 丁香五月婷婷激情蜜桃| 婷婷色激情网| 九九热青青草| 亚洲综合色网| 六月丁香av| 五月婷婷色丁香| 视频1区2区| 五月丁香综合激情| 久久久免费精彩视频| 色情性爱视频网址| 亚洲AV第二区国产精品| 操老逼综合网| 色色色com| 激情婷婷五月社区| 丁香五月婷婷呀| 久久99久久久久久久噜噜| 久久九九色| 五月丁香在线综合| 丁香五月老师| 99热这里只有精品26| 丁香五月天影院| 玖玖婷婷五月天| 五月开心色| 26UUU| se99热久久一本| 国产亚洲成AV人片在线观黄桃| 丁香五月欧美| 97久久超级| 99无码| 综合久久99| ...婷婷国产成人亚洲日韩| 色玖玖玖| 婷婷五月丁香综合桃花色网| 激情涩播| 国产精品久久久久久喷浆| 91大神操美女| 五月丁香影院| 色色色色色网站| 五月天婷婷爱丁香中文字幕| 激情五月婷黄版| 久久婷婷草| 少妇AB又爽又紧无码网站| 91美女被操| 深爱激情网综合| 五月天欧美 另类小说| 日韩九区| 热久久91| 色99视频| 色~性~乱~伦~噜| 日本黄色在线观看| 五月婷婷,狠狠操| 亚州男人天堂婷婷五月| 久久婷青青草原| 国产又黄又爽又色的免费| 天天狠狠夜夜狠狠2023| 99精品性爱| 综合婷婷| 99热97| av大香蕉| 干婷婷五月天| 色婷婷色五月丁香| 久久人人人人妻| 天天综合天天做天天综合| 丁香五月1页| 中文字幕综合网| 色婷精品91| 婷婷情色五月天| 五月婷婷七月丁香| 丁香五月婷婷亚洲综合精品| 色综合久久888| 色综合久久88色综合天天99| 婷婷丁香黄色| 超碰在线91| 天天日天天干天天插天天射| 一区二区三区四区无码| 丁香伊人激情| 99成人小视频| 激情婷婷五月少妇| 中文字幕丰满孑伦无码专区| 色色五月婷婷久久| 欧美天天性| 深爱开心激情网| 激情二色月| 五月丁香婷婷激情澎湃四射| 婷婷五月综合体验看| 婷婷五月色色| 丁香五月天堂网| 日日做天天操夜夜爽| 亚洲成人乱码av网站| 亚洲人人操BD| 日本一级| 成人亚洲精品久久久久| 五月天丁香婷婷社区| 色吧五月婷婷| oVV4WIB3vFi8D| 精品,99| 99久在线精品99re8| 日本一级一片免费视频| 少妇被下春药玩弄A片| 久久男人网婷婷| 色情婷婷。| 亚洲性天天| 五月婷婷九| 久久婷婷久久| 亚洲欧洲中文日韩久久AV乱码| 操逼电影免费看| 色噜噜97视频在线观看| \\五月天婷婷激情| 99热资源在线| 久草婷婷网| 99狠狠色| 天天爽免费视频| 五月丁香| 婷婷五月丁香色播| 国产六月婷婷| 99热都是精品| 五月天综合色| 丁香六月中文| 丁香六月啪啪啪| 免费观看全黄做爰的视频| 9热在线视频精品| 人妻自慰在线| 久久92| 日韩国产在线精品| 丁香六月婷婷综情欧美| 69色婷婷| av一区二区电影免费在线观看| 五月丁香综合| 我去色色网五雨天| 天天射天天操天天干| 五月丁香六月色婷| 激情六月丁香| 久久99jiu9| 2025最新亚洲激情在线| 五月丁香六月香香蕉| 日韩人妻在线播放| 99热这里精| 91丨九色丨43老版熟女| 九九热re99re6在线精品| 99色看| 亚洲精品视频在线播放| 婷婷五月天六点丁香五月| 婷婷爱五月天人人爱| 五月丁香啪啪啪| 激情综合九月| 色五月婷婷操逼| 久久婷婷五月综合一| 美女激情婷婷| 在线观看国产高清视频免费网站| 婷香五月网在线| 91avse| 日韩肏屄网| 丁香五月综合网亚洲综合欧美狠狠 | 天天草天天日| 69精品人人人人人人人人人| 激情另类综合| 色五月丁香五月| 无码啪啪| 天天操婷婷| 99色婷婷视频| 这里只有精品免费| 日本色噜| 免费无码毛片一区二区A片| 99久久66| 欧美性色A片免费免费观看的| 五月天国产成人| 日韩1区2区| 亚洲欧美婷婷五月色综合| www.五月天色色.com| 色九九综合热99| 婷婷久久精品| 婷婷五月天黄色| 色情五月丁香| 五月丁香综合激情在线观看| 欧美成人AAA片一区国产精品| 亚洲视频在线观看99| 能看的av| 亭亭五月天黑人2014| 六六久久黄色| 五月丁香综合激情网| 丁香五月ⅤA久久久| 九九九九这里只有精品| 中文久久婷婷| www.激情| 丁香五月亚洲无码| 91九九热| www.色综合| WWW·天天操·视频?| 8090在线影视少妇| 综合五月网| 大香蕉婷婷丁香| 九九五月天| 成人精品一区二区三区四区五区 | 久久久久激情| 免费观看欧美成人AA片爱我多深| 99re在线这里只有精品视频首页| 色婷婷瘦婷婷日韩| 九九热在线精品视频| 五月婷婷和六月| 色情婷婷久久五月天| 99热都是精品| 亚洲婷婷丁香五月在线| 99久久天堂婷婷| 丁香伊人网| 色情丁香五月天| 99热97美女| 亚洲操逼片| 日日干日日s| 五月香蕉网| 99热免费观看| 久色婷婷200| 天天久久66xxx| 中文AⅤ大全| www.色综合| 国产熟女日日骚五月丁香爱| 女婷久久| 日韩欧美性爱| 开心激情婷婷| 久99热在线观看| 天天综合.com| 97在线精品视频| 九九九九精品精| 亚洲在线资源| 天天做天天爱天天做| 丁香五月1页| 92久久久| 亚洲欧洲中文日韩久久AV乱码| 久久丁香五月| 九九热视频免费| 超碰在线成人| 日本三级毛片| 爱操天堂| 国产精品18久久久| 九九碰九九爱97超碰| 国产精品热搜丁香五月婷婷| 亚洲人人96@| 久久五月天色婷婷| 日韩精品VIP| 婷婷五月丁香五月| 成片免费播放| 婷婷的色色五月天| 日日艹思思热| 天天日,夜夜爽| 99在线观看视频免费| 大天天伊人| 丁香五月激情五月色综合| 五月婷婷六月丁香| 九九激情网| 99国产精品久久久久久久久久久 | 永久精品| www激情com| 99热这里有精品24| 77799热| 五月丁香婷婷综合网| 91久久久久久久久久| 婷婷五月综合丁香久久| 黄色视频网站在线播放| 99热免费| 欧美性猛交 XXXX 乱大交| 啪啪一区| 国产成人网址| 另类天堂| 色五月亚洲| 婷婷五月天久久久| 五月天色区| 99自拍视频在线观看| 久久免费操| 超碰不卡在线| 狼人狠狠操| 久久久99免费视频| 99婷婷国产最新视频| 99热这里只有精品在线| 色丁香五月婷婷| 婷婷中文字幕| 五月丁香色色| 五月天婷婷视频| 五月色婷婷综合| 99热官网| 亚洲成人无码专区| 亚洲日比视频| 99啪在线| 亚洲五月婷| 色一区高清| 亚洲欧洲色色| 亚州精品久久久久AV无码| 婷婷五月综合视频| 亚洲操B| 亚洲天堂啪啪| 五月激情综合网| 亚洲国产无线乱码在线观看| 亚洲无码影音| 五月亭亭欧美女人| 成人AV在线电影| 综合激情开心五月| 九九九九热99超碰| 丁香九月综合在线| 日本啪啪天堂| 天天摸天天舔天天爽| WWW.桔色成人.COM| 激情婷婷六月天| 99热e| 欧美在线视频99| 天天色2017| 五月天激情四射网站| 五月丁香| 久久久精品色色色| 久久九区| 影音先锋 萱萱| 一级二级色大片| 99re熱| 婷婷六月视频| 120分钟婬片免费看| 99精品视频在线观看| 一起操最新网址| 91视频一起草| 日逼免费视频| 伊人激情综合网| 国产SUV精品一区二区883| 五月天综合色| 樱花99视频| 久久精热| 亚洲亚洲人成综合网络| 久久色9| 五月丁香少妇网| www.激情| 婷色天堂| 丁香5月激情网| 99re思思热久久| 深爱激情五月网| 久久天堂女人| 亚洲激情五月婷婷日日| 六月丁香综合| 日本丰满久久| 91久久国产自产拍夜夜91久久精品文字>91麻豆精品国产 | 亚洲激情 久久| 国产亚洲色婷婷久久99精品91| 丁香五月天堂网| 91九色精品| SS丁香五月婷婷| 天天综合精品| 天堂成人久久| 亚美欧色影院| 欧美丰满熟妇BBB久久久| 区美毛片子| 这里只有久久精99| 影音先锋91网站在线观看| 超碰人人色| 日本爆乳片手机在线播放| 日韩丁香涩| 色五月xxx| 日本一级| 婷婷丁香亚洲色综合91| 91精品综合久久久久久五月丁香 | 色五月中文字幕| 激情久久天天| 99亚洲天堂| 五月色婷| 婷婷久久亚洲| 综合久久久| 天天操综合网| 婷婷色六月| 久久性爱视频这里只有精品| 影音先锋自拍网| 久久这里只有精品07 | 337久久| 天天干天天干天天干天天干天天干天天| 爱射综合| 色婷婷九月| 9|无码久久久久久| 色色色综合色| 91人人操人人| 大香蕉久| 99精品热| 99色在线观看免费| 九月婷婷久久| 亚洲 日韩色色| 日韩九区| 色播五月天天| 五月婷婷爽爽爽| 97婷婷丁香五月| 爱99干99| 男人的天堂五月丁香| 婷婷色色色| 五月开心深爱激情网| 久久38视频| 99这里只有精品视频| www五月天com| 丁香 久久| 大香蕉天堂| 久99热| 激情五月第四色| 99成人| 毛片新网地| www久久久久久久| 五月天啪啪啪| 97操在线视频| 亚洲激情综合| 五月天婷婷青青草| 成年视频免费观看| 色欲天天综合| 色网站99| 日韩AV成人电影| 啪啪操超碰| 成人日韩欧美| 久久久国产精品黄毛片 | 五月情综合| 激情精品久久| 久久久99视频| 五月丁香六月婷婷,婷| 久久久久久久久久久久久9| 色99欧洲色19| 狠狠爱婷婷丁香| 亚洲综合99| 99精品爱| 日韩 中文 欧美| 婷婷六月色| 色久五月天| 综合色色网| 色狠狠色噜噜AV天堂五区| 美国色五月天婷婷资源站| 9久热在线视频精品| 日韩啪啪视频| 婷婷五月另类网站| 亚洲天堂久久| 国产一级黄色影片,| 激情综合在线观看| 天天爽曰日爽| 久久婷婷五月综合精品蜜芽| 中字幕视频在线永久在线观看免费| 91wwmm导航| 久久99免费视屏| 大香蕉网 久久| 久久草大香蕉| 天天撸天天干天天插| 日本久久精品| 丁香五月婷婷香| 人妻久久久久久久| 日韩视频99| 九九综合精品| 久综合九| 婷婷五月丁香成人网| 婷婷丁香人妻天天爽| 五月激情偷拍| 亚洲日韩人妻操逼| XX久久| www.金莲av| 激情五月天影院| 开心深爱激情网| 天天干天天日天天插| 日韩在线99| 超碰69天堂| 99免费在线视频| 天堂综合久久 | 91精品刘玥| 久久婷婷视频| 99ri在线观看视频| 第五色婷婷| 思思热热久久| 欧美成性色| 9191avse| 金品在线视频99| 五月激情丁香啪啪| 久99| 天天操天天曰| 九九热99免费视频| 99精品视频免费观看| 久热这里只有精品66| 激情综合五月激情17| 亚州操操| 久久九九精彩| 久婷视频| 五月丁香色综合| 亚洲国产色色| 深爱丁香激情| 婷婷六月网| 久久人妻乱子伦| 丁香婷婷人妻| 日韩亚洲视频| 久久九九爽| 久久与婷婷| 啪啪黄页网| 久七香蕉| 日本在线99| 好色婷婷| 碰97久久| 天天操婷婷| 国产熟女大叫受不了| 婷婷丁香五月色偷偷| 日本123区日韩欧美不卡在线看| 91丨九色丨首页| 久99久视频精品| 五月丁香六月激情| 色婷婷综合影院| 亚洲综合激情五月久久| 思思久久99| 激情综合激情综合| 99性爱视频| 五月天天天天天天天天天天天婷婷婷| 亚洲激情网| 久久玖玖综合| av在线资源| 五月丁香影院| 久久精品99国产精品日本| 五月开心播播网| 九色成人AV在线| 五月婷久久综合| 久久99久久久| 五月丁香色| 久久久久久久丁香五月天婷婷| www.粉嫩av.com| 色婷婷五月天激情久久| anquye五月| 99国产视频网| 日韩精品999| jiZZdr| 国产成人精品一区二三区熟女在线| 人妻熟女一区二区AV| 99久久超级| 久久婷婷激情四射五月天| 情色五月天网站| WWW·天天操·视频?| 99亚洲欧洲| 五月丁香婷婷综合视频| 91超碰在线观看| 五月综合色| 久久玖玖99| 激情久久肏屄视频| 这里只有精品视频在线观看免费| 亚洲VA在线| 影音先锋偷偷色男人站| 五月婷婷六月丁香在线| 超喷97免费在线视频| 人人97操| 激情综合啪啪啪| 五月天色网站| 97视频.干com| 97在线观视频免费观看| 高清国产一级婬片a免费| 99久在线精品99re8| 丁香五月AV综合激情| 99色热| 五月婷婷综合在线| 九九黄色网| 日曰躁夜夜躁2026| 99精品人人| 激情五月天之五月婷婷| 五月欧美丁香在线观看| 伊人综合网站| 香蕉大综综综合久久| 九九热只有精品6| 五月丁香六月色| 久久人妻精品| 九色无码| 九九久久视频| 噜噜视频| 天天综合91入口| 婷婷亚洲激情在线观看视频| 婷婷综合五月| 久久久天堂国产精品女人| 色五月婷婷在线| 性生活久久人妻| 激情五月久久| 性爱五月丁香| 久久色五月天综合网| 五月婷视频| 免费看无码视频A级| 26uu| 丁香五月成人| 色玖玖玖| 天天天天天久久久久久| 99综合视频| 亚洲综合在线播放| 色五开心五月五月深深爱| 人人操AV| 任你日视频| 啪啪小说五月天| 97亚洲视频在线| 99惹在线精品免费观看| 91精品国产99久久久久久天美| 丁香婷婷欧美综合| 久久这里只有精品07 | 五月婷婷AV| 色播播婷婷| 天天插天天操| 桃色五月婷婷| 狠狠狠狠狠狠狠狠狠狠狠色宗合图片| 亚洲永久四色| 九九大香视频| 丁香五月偷拍| 九九热青青草| 丁香成人色情五月天| 久草热8精品视频在线观看 | 青青草婷婷五月天| 天天色综合色色色色色。| 久久新地址| 九九大香蕉黄色影院| 99热99热在线| 中文字幕高清av| 五月丁香六月花| 一本色综合色| www.五月激情红色| 亚洲精品一区无码A片| site:pzdcoin.com| 俺去也综合| 噜噜噜噜婷婷五月天| 激情五月婷婷综合| 色~性~乱~伦~噜| 激情伊人| 99色色网| 天天插天天爽| 丁香无五月网| 国产99久久久国产精品免费看 | 丁香六月久| 亚洲激情六月丁香| 五月婷婷开心五月| 欧美操综合| 大天天伊人| 91色噜噜狠狠狠狠色综合| 色婷婷a| 丝袜人妻| 色色色色综合网| 天天做天天双| 性爱激情五月| 色婷婷亚洲精品天天综| 天天se在线视频| 狠狠久久婷五月| 激情网站五月| 国产人妻777人伦精品HD| 婷婷五月综合在线视频| 色婷婷四虎| 神马欧美精| 日韩中文欧美| 欧美性猛交99久久久久99按摩| 激情久久五月网| 久草免费福利视频| 天天在线XXX| www.99热视频| 国产精品18久久久| 思思热久久久在线| av操一操| 成人丁香五月| 开心日韩丁香婷婷五月| txt五月激情四射网综合俺也来了| 婷婷五月天美女21p| 97九色| 国产精品涩涩涩视频网站| 久久A区B区| 成人精品人妻| 人妻丰满精品一区二区A片| 常久最新免费的色吊丝| 91丨九色丨丰满人妖| 色九月| 激情深爱综合网| 丁香九月激情| 色综合久久久综合久久网 | 超碰狠狠操| 婷婷丁香五月亚洲| 精品激情| 九热网站| 五月婷婷综合激情| 中文字幕色色色| AV在线大香蕉| 影音先锋 萱萱| 91色综合网| 丁香六月毛片| 激情综合五月天| 五月天开心色色网| 成人av在线电影| A A色色| 久久婷五月婷| 婷婷五月天综合久久日美女| 在线播放中文字幕| 亚洲色亚洲精品| 丁香五月激情六月欧亚激情综合导航| 欧美大片| 午夜色婷婷| 六月丁香成人网| 欧日美女Va| 成人毛片在线免费观看| 无码人妻丰满熟妇奶水区码| 婷婷六月天天| 97在线视频 欧美| 色情五月综合婷婷| 国产欧美va| 丁香五月日本| 色色五月婷| 久草五月婷婷| 青青草婷婷久久| 韩日AV片| 六月丁香啪啪啪| 丁香 久久| 婷婷五月天干干| 影音先锋女人av鲁色资源网小说免费| 超碰97色| 色色欧美色色色| 九九这里有精品| 国产成人AV在线播放| 淫水导航| 深爱激情五月天色婷婷| 国产精品电影| 蜜桃婷婷狠狠久久| 97超级碰碰碰久久久| 大香蕉婷婷| 五月婷丁香| 91人人操.COM| 另类小说五月天| 激情小说婷婷五月| 五月色天五月色| 激情五月天综合图片小说网站| www.色综合| 另类激情五月| 婷婷趴趴| 六月婷婷AV| av一区免费看| 99热最新国内| 99视频这里有精品| 99色色网| 密桃激情五月天综合网| 五月丁香婷婷五月色| 深爱五月激情网| 久久久精品99亚洲综合| 色爱综合网| 欧美三级欧美一级| 九九热99免费视频| 免费看成人747474九号视频在线观看| 五月丁香花激情啪啪网| yw国产AV| 久9热在线免费观看| 色在线免费观看| 午夜在线成人网站免费观看| 欧美十二区| 老司机视频lsj爱就色| 丁香婷婷五月色综合| 99@久久@99精品视频| www.ywav| 大香蕉懂9| 日本97久久久精品| 色五月婷婷影院| www.jiujiujiu| 九九精品99| 五月丁香六月激情综合网| 亚洲综合网区| 天天噜天天爱| 久久这里只有精品无码| 操一操| 丁香桃色网| 五月天婷婷綜合院| 婷婷综合色色| 在线播放成人网站| 亚洲无AV在线中文字幕| 另类婷婷五月天啪帕帕| 极品九九九九九九| 丁香五月婷婷色| 国产成人精品一区二三区熟女在线| 青青草视频免费观看| 六月婷婷色色网| 婷婷色五月天色色| 91丨九色丨白浆秘| 久久无码激情视频| www好屌操| 深爱激情婷| 丁香五月欧美激情| 99啪啪| 五月天丁香婷婷视频网址| 丁香五月婷婷欧美成人色图| 狼人狠狠操| 激情综合青草| 天天射综合网夜夜操| 婷婷性福五月天| 久久99网| httpwww色com日本| 9人人操人人看| 久久成人性爱| 久久只有18视频| 久久丁香五月天| 国产黄色在线观看| 伊人婷婷五月天av| 五月天婷婷激情网| 久久AV电影| 久久桃花网色婷婷| 91无码视频| 黄桃AV无码免费一区二区三区| 日本女色人人| 97人操人免费视频| 日本狠狠爽| 激情五月综合婷婷| 色婷婷小说网| 日本色超碰| 九月丁香婷婷| 五月停停激情网| 婷婷金品综合视频| 色婷婷亚洲综合网站| 九色视频91疯狂| 操逼福利视频| 99爽视频| 婷婷丁香六月天| 91婷婷在线| 狠狠色综合久久| 五月婷婷激情综合| 丁香六月综合激| 97色五月天| 色欲天天综合| 午夜成人av在线| 丁香五月天婷婷在线视频| 久久五月综合| 久久久久久久人妻| 五月激情射| 深爱激情av| 精品久久久人妻| 看逼中文字幕| 五月丁香网站在线播放| 97精品自拍| 丁香婷婷五月综合欧美另类| 久久永久网址| 婷婷娌伦网| 亚洲欧美成人在线观看| 人人干天天舔| 97在线刺激| 大香蕉久久久久久久久| 丁香六月 人妻| 丁香六月婷婷| 婷婷欧美激情| 战争与艾拉电影免费观看| www.色婷婷.com| www.五月天色色色| 色五婷婷| 无码一区精品一区视频| 丁香五月激情宗合网| 欧美性色视频| 九九色黄色| 五月丁香婷中文| 99久视频| www.91色| 大香蕉婷婷丁香视频在线| 天天久久九九| www 五月天 com| 激情深爱五月天| 五月丁香六月色婷| 色婷婷亚洲| 丁香五月婷婷亚洲色图| 九九亚洲视频| 激情九月丁香婷婷| 天天干天天日日| 亚洲综合五月天| 激情五月天在线观看色婷婷| 99久久综合网| 色婷婷啪啪啪啪啪啪| 六九色综合婷婷五月天| 日本丰满久久| 久久久精品色色色| 91呦呦呦| 99亚洲无码| 九月婷婷综合在线| 99视频只有这里精品| 色涩视频久久| 亚洲性色XXXXX| 久久久全国免费视频| 色婷婷五月天综合网| 婷婷久久色五月婷婷久久久| 丁香五月亚洲综合| 激情四射亚洲| 久热精品视频| 狠狠色丁香久久综合婷婷亚洲成人福利| 5月婷婷五月天| 丁香五月激情欧美| 182TV大香蕉|