免费国产精品自在自线-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月婷婷视频网站综合| 淫荡综合网| av一级棒av| 丁香五月激情五月开心五月| 婷婷五月天另类视频| 97操男人的天堂| 色婷婷伊人| 亚洲AAA| 久久亚洲无码| 色婷婷精品视频在线播放| 激情人妻综合| 5月丁香啪啪啪| 五月丁香六月久久| 色婷婷久久久| 97影院一级片| 久久色9| 八戒青柠影视剧在线观看| 可以免费观看的AV| 天天综合中文| 久久五月综合| 日日懆天天懆| 天天成人丁香美女AV| 99色网站| 99自拍网| 99碰网站| 五月婷婷无码| 91久久婷婷| 9热在线观看| 91se视频| 影音先锋 萱萱| 天天肏夜夜肏| 五月丁香影院| 另类图片五月天| 五月丁香激情四射| 婷婷六月啪啪| 色吧五月婷婷| 日本片日本片祼观看网站在线看中文版网页在线看 | 黑人熟妇一区二区三区| 奇米网大香蕉| 激情综合网五月| 97久久精品视频| 疯狂做受XXXX高潮A片| AV在线免费播放| 99爱视频在线播放| 五月丁香香蕉| www.99日本| 丁香五月乱中文字幕| 日本的α片xxxwww| caopeng97日韩| 新99思思视频| 激情小说之五月| 色五月激情五月丁香五月婷婷啪啪综合 | 亚洲激情综合五月婷婷啪啪| 99re视频精品| 婷婷五月丁香综合激情| 五月丁香六月婷婷激情网| 激情亚洲网| 天天干com| 午夜不卡成人一区二区| 天天插天天操| 五月色丁香| 日本九九网| 激情五月婷婷五月| 国产精品久久久久久白浆色欲| 婷婷五月花| 五月丁香六月| 婷婷丁香18| 丁香五月婷婷五月| 97热91| 99国产在线精品视频| 国产毛片精品一区二区色欲黄A片| 激情五月婷婷视频一区二区三区| 成人αV视频免费观看| 亚洲国产精品VA在线看黑人| 97人人操人人拍| 精品五月花| www色哟哟| 丁香五月综合激情久久潮喷| 欧美人人操| www.超碰在线| 亚洲 五月 婷婷 成人| 深爱激情六月| 五月婷婷无码| 97干在线视频精品店| 五月丁香综合网| 日本九九网| 日本久久精品| 婷婷操婷婷干婷婷射| 色丁香五月婷婷| 九九99九九99| 黄急一级视频| 五月婷婷在线丁香| 99精品在这里| 少妇被下春药玩弄A片| 久久九九思思| 26uuu视频欧美| 久久深爱激情网| 国产色色色色| 婷婷五月天在线观看| 日本人人超碰| 99无码超碰| 日日日,com| tingtingzonghewang| 五月婷激情| 91丨九色丨大屁股| 激情五月天情色| 丁香六月啪啪啪| 五月丁香天天| 开心激情网在线| 精品久久久久成人码免费动漫| 亚洲啪啪精品| 99热在线中文字幕| 九九色热| 玖玖爱导航| 久热这里只精品| 欧美英丁香开心快乐六月天网| 热久精品| 丁香五月婷婷啪啪视频| 美女亚洲五月丁香| 亚洲区视频| 大香伊人久色| 激情文学天天| 婷婷五月天久久| 这里只有精品96| 综合九九中文字幕| www激情com| 天天天天操| jiuse91在线| 天天插天天插天天日| 夜夜夜夜夜骑撸| 99视频自拍| 五月丁香六月婷婷激情四射| 丁香六月成人网| 大香蕉综合在线| 热91久| 九九精品综合| 久9热视频| 一片AV片免费播放| 五月丁香六月婷婷a v| 久9久9热久热| 成人av播放| 五月婷av| 琪琪狠狠干| 99re久热只有精品6在线直播| 伊人婷婷大香蕉| 亚洲成人在线电影网站| 99热这里只有精品69| 大香蕉啪啪啪| 五月丁香六月婷| 欧美成人色婷婷| 黄色片久久| 五月天丁香六月综合| 丁香婷婷情色五月天| 婷婷五月性感| 丁香社区婷婷五月| 2018夜夜草| 日日日日做夜夜夜夜无码 | 日本nghangse中文字幕| 婷婷激情六月天视频| 亚洲九九视频| 六月婷婷无码| 亚洲爆乳无码精品AAA片蜜桃| 亚洲激情综合网| 第四色大香蕉| 中文字幕永久免费| 成人片在线播放| www.henhenl| 五月开心网| 99精品丰满| 五月停停丁香| 美国不卡视频| 婷婷四色五月| 色五月婷婷 成人| 五月婷婷久久综合| 丁香六月婷婷久久综合八月| 色婷婷丁香A片区毛片区女人区| 成人深爱丁香五月| 噼里啪啦完整版中文在线观看| 99热99日…..| 在线18av | 五月综合在线婷婷图片| 丁香五月激情综合久久| 97碰碰视频| 《久久综合九色综合97婷婷| 婷婷啪啪| 色五月婷婷、老熟女| 国产毛片欧美毛片久久久| 99这里精品| 大地9中文在线观看免费高清| 91艹人| 国产,欧美,学生妹,视频| 婷婷六月激情啪啪| 98热精品| 99色在线视频| 日本激情综合| 男人天堂99| 精品久久人妻| 天天天天天久久久久久| 日本色色网站| 无码少妇高潮喷水A片免费| 久久香蕉婷婷五月天| 2018夜夜草| 天天操屄网| 欧美色碰| 久久婷婷色| 天天草天天舔| 五月亭亭欧美女人| 狠狠色色| 99视频| 日韩欧美颜射| 就爱日五月天| 欧美日韩999| VA国产在线综合网站| 五六月丁香激情视频| 久久婷婷人人| 亚洲成av人影院| 欧美成人网婷婷综合在线| 色播jjjj| 丁香九月婷婷| 日本九九热| 91热手机在线| 狠狠五月天| 快色t v在线入口| 三级毛片7979| 久久五月婷天天干| 色播五月婷婷| 婷婷综合激情| 人妻内射麻豆视频| 亚洲超级碰| 成年人丁香五月| 婷婷五月丁香成人| 婷婷五月天奸女| 无码少妇高潮喷水A片免费| 婷婷五月精品中文字幕| 视频这里只有精品| 激情五月份婷婷| 99精品视频在线观看| 日日舔夜夜操| 色亭亭五月天网扯| 91seav| 香蕉97碰碰碰欧美| 综合激情视频| 久99热在线观看| 天天色五月| 日韩高清久久| 婷婷天堂综合网| 激情五月天婷婷| 天天婷婷综合亚洲亚洲| 欧美性爱5月天天天看| 五月色色网| 亚洲va久久久噜噜噜久久天堂| 久久激情综合| 天天日天天插| 五月丁香中文字幕| 丁香色情五月综合网站| 激情欧美婷五月| 97在线刺激| 1024AV视频| 久久久婷婷| 婷婷六月综合激情| 欧美3AaAa大片| 日日噜狠狠色综| 人橾人| 大香蕉AV电影在线| 草草女人亚洲| 精品少妇人妻AV无码专区偷人 | 色色射| 色国产五月| 亚洲婷婷91丁香| 日韩啪啪自拍| 婷婷九月久久| 色天使色综合| 超碰免费人人肏| 欧美日本日韩| 欧美性生交XXXXX无码小说 | 激情六月天婷婷| 五月天婷婷久久综合| 婷婷爱在线观看| 丁香婷婷五月综合| 天天干天天操天天射| 精品AV无码超碰| 欧美大片免费播放器| 色五月激情| 亚洲综合99| 久草a片| 人妻精品一区二区三区| 久久人妻人人| 久久综合影院| 久久久久久综合88| 狠狠干婷婷| www.jiujiujiu| 另类激情五月在线视频欧美| www.色婷婷| 免费视频99| 丁香桃色网| 四月婷婷丁香| 日本五月天激情| 91精品综合久久久久久五月丁香| 婷婷五月天影视首页| 九九热思思| 激情综合色婷婷六月天| 日本一级一片免费视频| 无码操B| 天天色综网| 日本色婷婷| 色五月天堂| 九月婷婷久久| 国产毛片欧美毛片久久久| 120分钟婬片免费看| 亚洲精品一区中文字幕乱码| 99国产97在线,| 五月色情婷婷| 99精品视频偷拍| 91久久精品无码一区二区三区| 免费亚洲婷婷中文字幕| 狠狠色性| 天天色综网| 五月天久久婷婷| 婷婷99狠狠| 天天肏高清在线| www久久99| 九九热免费视频| 97在线观视频免费观看| 丁香六月色| 日韩高清成人| 国产探花AV在线| 99热在线观看精品| 91九色首页| 日本精品人妻无码77777| 天天日中文| 91干在线视频| 欧日美女Va| 色婷五月丁香久亚洲| 婷婷五月天综合小说网| 91久久婷婷| 无码日本精品XXXXXXXXX| 影音 五月 婷婷 久久| 色射7856五月天激情四射| 婷婷爱爱蜜臀天天操| 六月丁香深深爱综合网| 这里只有精品免费观看网占| 五月天色影院| 色欲色香综合网站| 久久99这里| 天天爽天天干天天| 婷婷五月激情丁香| 91啦丨九色丨刺激中文| 五月天婷亚洲天综合网综合| 天天干天天日天天操| 丁香五月天啪啪| 影音先锋91网站在线观看| 亚洲精品网址| 秋霞电影理论| 人妻无码精品一区| 欧美日韩精品一区二区三区钱| 大香蕉婷婷色| 日日夜夜狠狠操| 79精品视频在线观看,| 日韩在线看AV| 激情小说五月天社区丁香| 婷婷五月天美女视频| 久久色婷婷| 99九九精品视频推荐| 外国碰视频网站97| 亚洲激情网站| 国产AV一区二区三区最新精品| 婷婷丁香社区| 色色五月婷婷丁香| 国产欧美熟妇另类久久久| 久久精品系列| 欧美成人网99网| 涩五月婷婷| 黄久久久| 天天插,天天射| 国产精品第一国产精品| 天天玩夜夜操天天爽| 五月丁香婷婷基地| www.婷婷激情网.com| 淫视馆aV二区一区| 五月天激情综合| 国产成人综合亚洲| 最新激情五月天| 99这里有精品视频| …亚洲黄色在线播放日韩、av中文a…| 五月丁香婷婷激情| 六月丁香啪啪| 激情婷婷丁香五月天小说| 婷婷五月天国产性感美女演员久久久久| 99热国内精品| 天天干天天拍| 九九99在线| 五月婷婷婷自由综合| 久久综合99| 亚洲第一黄网| 色婷婷综合网站| 亚洲成人九九九| 99在这里有精品| 久久er99热精品一区二区 | 婷婷五月免费视频| 亚洲AV永久无码影院黑人| 91免费在线视频6| 六月激情婷婷| 五月丁香在线视频观看| 亚洲激情婷婷| 琪琪秋霞| 人妻丰满精品一区二区A片| 日韩亚洲视频| 色5月婷婷| 超碰九九热| 久99久99精品免| 久久天堂精品| 99在线免费视频| 六月婷婷七月丁香| 99re视频在线播放| 色九九综合| 成人精品在线观看| 色就是色婷婷五月亚洲激情| 婷婷五月天色播| 色色色999| 东京热免费视频| 操久久网| 超碰在线99| 激情婷婷内射| 在线观看免费观看在线9久| 五月婷婷六月激情在线| 97色精品视频 | 在线观看免费视频| 五月婷婷丁香色吧网| 91操黄| 亚洲色在线观看| 五月天大香蕉AV| 五月天婷婷久久| 91综合在线视频| 五月丁香好婷婷A片网| 人妻AV在线观看| 丁香久久| 蜜臀九九九九| 色天堂操| 五月丁香婷婷基地| 99热这里有精品24| 成人AV网站在线| 91久久五月天| 婷婷基地成人五月天| 日本人妻伦在线中文字幕| 五月天激情.com| 丁香激情五月天| 五月婷婷六月丁香玖玖玫瑰91| 欧美 日韩 成人在线| 99热亚洲| 伊人五月成人| 99热老网站| 最近中文字幕大全免费版在线| 国产精品久久久久久五月天加勒比 | 99久热| 五月婷婷色吧!| www.com色播五月天| 激情久久五月天| 99色啊| cao视频,现在观看| 天天操婷婷| 我要射综合| 99人人操人人爱久久久| 亚洲亚洲人成综合网络| 99热只有| 五月天婷婷丁香视频| 久久婷婷五月综合色天| 色色com| 久久机热思思热| 九九婷婷综合| 激情操逼婷婷| 99思思| 激情五月婷婷在线| 99re99在线看| 另类国产区| 大香蕉久| 五月狠狠| 天天透天天摸天天舔| 91精品婷婷国产综合| 亚洲亚洲人成综合网络| 婷婷五月天堂| 亚洲色爱综合| 色天天综合色| 9久精品| 亚洲电影中文字幕| 激情99| 9l视频自拍九色9l视频在线观看| 亚洲日本韩国| 色婷五月| 久久爱婷婷| 五月婷婷狠天天色综合| 99热这里只有精品1025| 激情婷婷五月综合| 色狠狠色综合久久久绯色aⅴ影视| 久久Xx| 性生活视频98791| 五月婷婷在线丁香| 日本高清久| av首页在线| 激情综合五| 婷婷激情性爱| 国产无人区大片| 五月色丁香激情| 另类激情五月天。| 国产精典视频在线观看| 日韩xx在线| 婷婷五月综合啪| 日本妈妈乱| www.9797国产| 色色色色色五月| 色婷婷香蕉在线| 久热无码| 久久99久久99久久99人受| 日韩在线视频中文字幕| 精品色色| 五月婷婷开心网| 亚洲人人操BD| 天天干天天玩天天夜天天射天天操天天日蜜臀少妇 | 色操综合| 67194线路二在线观看| 大地9中文在线观看免费高清| 超碰99在线观看| 能看的av| 亚洲无码11| 五月天啪啪啪| 色导航色婷婷五月天在线观看| 九九热在线观看视频网站| 综合亚洲色色| 婷婷五月丁香六月伊人网| 色999;丁香五月| 99日在线视频| 色欲久久综合| 天天色天天搡| 99热在这里只有免费精品| 99热乎| 丁香色五月直播| 99色综合| 丁香五月色综合色播五月| 精品国产va久久久久| 俺也去在线久久精品23欧美综合视频网站,丰满人妻一区二区三区在线视频53,丰满 | 欧美激情-区二区三区| 五月婷婷六月爱| 日本本土色网第一区| 婷婷五月激情四月综合| 日操| 九九热区一区二区三区| 色婷婷五月天堂资源| 99精品7| 丁香五月在线观看完整版| 99精品在线下载| 激情5月天天天| 激情五月,深深爱五月| 激情综合亚洲色婷婷五月| 久久性爱视频久久性爱视频| 综合在线色婷婷| 日本色色网站| 五月天婷婷激情在线色图| 能看的av网站| 狠狠狠五月婷婷六月丁香| 色色色.com| 精品三区影院| 熟女激情五月天 | 变态另类色图| 色在线99| 思思视频这里是精品| 婷婷丁香五月天小说| 五月丁香久久丝袜啪啪| 噜噜噜久久| 五月花激情| 精品一二三区久久AAA片| 久久97久久99久久综合欧美| 色五月中文字幕| 婷婷丁香97| 26UUU成人网| 欧美激情2025| 成人av在线电影| 综合久久婷婷| www.99热精品99.com| 久热黄色| 天天操天天谢| 精品99在线| 色婷婷综合网| 综合九九久久| 丁香五月综合激情久久潮喷| 午夜做爱影院| 久久久免费精彩视频| 草草色情综合网| 91九色小视频| 色情五月天首页| WWW.桔色成人.COM| 免费看欧美成人A片无码| 大大香蕉综合在线| 久久黄A片| 开心婷婷五月中文字幕组| 影视av久久久噜噜噜噜噜三级| 337p大胆噜噜噜噜噜91Av| 五月婷婷综合在线视频| 国产在这里只有精品| AAA级久久久精品| 亚洲五月激情| 天天综合天天玩夜夜玩天天玩夜夜玩 | 色色影院黄大片| 97成人丁香婷婷| 99综合免费视频| 激情五月小说婷婷| 色五月,婷婷大香蕉| 丁香六月色香蕉视频| 狠狠做深爱婷婷久久综合一区| 亚洲AV免费在线| 啪啪视频99| 激情小说婷婷小说| 色色五月天婷婷丁香| 国产3p露脸普通话对白| 六月婷婷激情| 天天影院色| 国产亚洲精品AAAA片APP| 六月激情婷婷综合| 亭亭五月激情亚洲在线| 午夜一区| www.久久久.com| 九九精品在线观看视频6| 激情五月小说婷婷| 中文久久婷婷| 久久这里有精品视频| 人人爱操| 婷婷激情四射| 欧美大香蕉视频| 婷婷五月丁香99| 丁香五月亭亭六月综合激情网| 26uuu丁香婷婷五月| 九月婷婷综合色干| 婷婷丁香激情五月| 天天插天天插| 婷婷综合| 色婷婷综合网站| 性爱人人网| 99热这里只有免费精品| 久久激丁香| 天天婷婷天天| 婷婷五月天av| 九九热色视频| 欧美人妻一区二区| 午夜成人天堂久久无码日韩久久| 五月丁香婷婷综合| 丁香久久九九99| 五月婷婷久草在线视频综合| 99在线资源视频| 97色色色色色色色色色色色色色| 九九色综合| 五月色情婷婷开心五月天| 99视频自拍| 婷婷性爱网| 丁香五月婷婷AV| 国产日韩av片| 亚洲六月婷婷| 色五月婷婷久久| 99久热| 噜噜噜噜婷婷五月天| 亚洲精品视频在线| www,五月天com| 色色色色网| 欧美va精品va老师va| 欧美性爱五月天| 天天做天天爱天天日| 久操热| 人人色性网| 国产人妻人伦精品一区二区| 人人干AV| 丁香伊人五月色婷婷五十路| av人人干| 中文字幕综合网| 五月激情小说网| 激情五月天婷婷久久久久久久久久久| 五月婷婷色| 九九九午夜影院成人| 玖玖色综合色| 色五月中文网| 亚洲成人在线免费| 无码色| 国产寻花在线| 六月婷婷日| 夜色综合网| 色激情综合| 99爱精品| 五月天激情AV| 殴美激情综合网| 中文av网站| 秋霞网在线观看理论91| 亚洲天堂制| 99碰碰视频| 激情六月下句是什么| 精品人妻午夜一区二区三区四区| 激情五月丁香六月综合AVXXXX| 精品99视频| 天天影院色| 婷婷性爱无码视频| 综合日本婷婷| 六月丁香激情网| 五月噜噜| 久久婷婷五月天激情四射| 五月丁香影院| 啪到高潮激情丁香五月| 色色免费网站| 伊人婷婷福利网| 91青娱乐青青草| 色婷婷中文字母五月丁香| 日韩中出视频| 99精品在线| 亚洲色欲AAAAAA| 天堂综合久| 五月丁香| 婷婷五月影院| 天天 日综合| 五月婷婷综合影院| 2018国产大陆天天弄| 婷婷六月激情综合| 色10月婷婷视频| wwww.9免费视频| 天天操天天操天天操天天操天天操天天操天天操天天操天天操 | 五月婷婷色色色| 婷婷六月情| www.99视频| 级人人91| 成人 在线 日韩| 五月激情丁香| AV伊人青草丁香六月| 九九九九热99超碰| 99超级碰免费视频| 丁香五月婷婷激情蜜桃| 婷婷丁香77777| 国产精品丝| 2025超碰| 欧美精品999| 天天综合色丁香| 色五月成人在线| 欧美日韩成人在线| 影音先锋91资源站| 丁香情色五月| 亚洲丁香花色| 久久多色| 久久这里只有精品8| 日日婷婷不卡| 色婷婷基地在线| 久久婷婷人人| 色婷婷五月综合色婷婷| 一本色道久久88综合日韩精品| 色婷婷久久久| 婷婷 丁香 精品| 99性感视频| 婷婷五六月丁香| 久久作爱| 丁香五月开心亚洲| 人妻Av在线| 伊人热在线大香蕉| 国产亚洲精品久久久久久豆腐 | 久操乱| 涩涩五月天| 九九99免费视频| 色99在线观看| 另类综合婷婷五月天欧美视频| 狠狠五月丁香色婷| 五月婷婷丁香啪啪| 九九99精品视频在线观看| 人人操人人爰人人一天天碰夜夜拍夜夜爽-中国A级毛片天天看天天谢… | 久热9| 影视av久久久噜噜噜噜噜三级| 五月婷婷性爱视频| 丁香五月色激情| 九九色情网站| 九久久婷婷| 成人亚洲精品久久久久 | 色色色无码| 狠狠搞亚洲| 久久久久人妻中文| 丁香激激情网| 夜夜撸日日骑| 丁香五月综合网亚洲综合欧美狠狠| 国产精品-第3页-91JQ就要激情网91JQ5.JQJQ926.XYZ | 五月丁香在线观看99| 99综合成人视频在线观看| 免费一区二区三区| 草榴视频黄色网| 日本成人内射| 久草婷婷视频| 97精品综合久久内射| 日韩AV中文字幕在线| 亚洲av无码影院| 色五月97| 国产免费一区二区三州老师F1F1| AV色婷婷| 婷婷色导航| 成人五月天婷婷| Av性爱网站| 色婷婷丁香五月天在线视频| 婷婷丁香五另类网站| 管管補管管紱| www国产亚洲色婷婷com| 啄木鸟丝袜美女福利视频| 天天干天天干天天干天天干天| 六月综合在线| 99riAV国产精品视频| 亭亭色色五月天| 色无码| 熟妇人妻中文字幕无码老熟妇| 超碰在线日夜| 精品一区二区三区三区| 婷婷丁香五月天影院 | 五月丁香婷婷爱| 天天搡日日搡aaaaⅩ| 五月婷婷av| 天堂无码人妻精品AV一区| 超碰亚洲欧美| 99久久五月天| 日韩综合网络男女香蕉a片| 另类的婷婷| 色 五月婷婷基地| 天天操天天操天天操天天操天天操天天操天天操天天操天天操 | 婷婷五月天成人基地| 99热这里只有精品86| 久久婷婷综合国产| 思思热精品在线| 玩熟女五十AV一二三区| 97碰碰人人视频| 五月天综合久久| 婷婷激情五月天在线视频| 亚洲性图一区二区| 五月天婷综合| 性爱视频久久| 天天综合色99| 九月婷婷综合| 国产精品色一哟哟| 色在线99| 99噜噜噜在线播放| 伊人三级激情| 成人色色综合| 天天爽夜夜爽夜夜爽精品视频| 五月丁香激情综合网| 成人av在线网| 久碰婷婷视频| 婷婷色五月久久| 99re这里只有| 成人无码髙潮喷水A片| 五月天激情黄色网址| www.婷婷五月天,com| 常久最新免费的色吊丝| 操97| 操逼综合激情网| 亚洲精久久| 俺去也五月天婷婷| 开心五月深爱五月| 日本欧美999久久久三级片| 激情色播| 五月天婷婷激情综合| 婷婷天堂站| 久久一品区| 99色啊| 99久精品视频| 97久久综合网| 五月开心婷婷中文字幕| 夜夜骑天天操| 精品少妇人妻AV无码专区偷人 | A久久| 欧美三级巜人妻互换| 五月婷婷色播| 五月色网| 亚洲妇女熟BBW| 99色中文| 色五月女| 国产亚洲精品久久久久久久久动漫| 女操碰| 91精品综合久久久久久五月丁香 | 亚洲欧洲午夜成人精品av| 日韩人妻白浆视频系列| 五月天激情综合在线| 欧美性爱五月天| 爱99干99| 欧美天天五月丁香免费观看| 婷婷97碰碰| 久久五月天丁香花| 天天拍夜夜爽| 91精品91久久久中77777| 九色91视频| 成人精品亚洲性爱| 色五月综合激情网| 国产va在线视频| 五月天婷婷在线播放| 九九色院| www.99情趣网| 色色亚洲99com| www·五月天| 99操逼| 色吧五月| 久久狼人天堂| 久久久久99精品成人网站| 六月婷婷久久大全| 五月婷三级片| 思思热在线精品视频网站| 大香蕉av在线| 99re热精品视频国| 五月天精品视频| 超碰97人人操| 激情综合五月天| 久久香蕉福利| 天堂中文国产| 综合色网站| 激情五月天婷婷| 九月丁香八月婷婷久久综合久97| 综合九九| 婷婷五月天性爱视频| 婷婷色五月综合| 99视频日韩| 狠狠色噜噜| 婷婷五月天大香蕉在线视频观看| 色五月丁香六月婷婷| 教师性爱毛片| 丁香五月天天高清在线| 久久日婷婷| 丁香五月天在线| www,99色| 久色五月丁香视频| 97黑人精品区| 色情五月婷| 丁香婷婷五月激情| 五月婷婷丁香| 久久er这里只有精品| 丁香五月偷拍| 亚洲舔观看| 激情玖玖sh| 五月婷婷自拍视频| 91人人爽人人操| 天天爱天天狠天天透| 91免费看片| 五月天社区| 五月婷婷伦理| 日韩乱轮AV| 色婷婷五月天视频在线| 色一情一乱一乱一区9| 婷婷五月天美女21p| 久久免费操| 久久久这里有精品| 天天色天天干天天插| 九九九激情网| 婷婷丁香五月色| 五月天天堂久久| 色综合五月天| 五月婷婷无码专区| www.久操| 99在线播放视频| 婷婷五月天受日本法律保护| 国产AV一区二区三区日韩| 99思思热只有在这里看| 婷婷五月中文字幕| 色墦五月丁香| 成人网站免费sxj| 婷婷丁香五月天色色| 国产在线黄色| 天天摸天天舔天天爽| www天堂99| 操人久久| 婷婷大美在线| 看逼中文字幕| 婷婷色五月天在线观看| 狠狠色激情综合| 久久99三级在线视频| 另类图片天天影视在线观看| 五月天成人伊人| 色爱综合五月| 婷婷五月天电影网 | 少妇2做爰HD韩国电影| 九九99一区| 97人人超| 操97在线观看| 日韩啪啪网| 九九热这里只有精品12| 91视频免费后入强操| 操一区| 久久久久人妻网址| 婷婷激情中文综合| 五月天婷婷无码| 色综合婷婷| 婷婷六月天国产综合| 人妻少妇色综合| 99色色热| 69精品人妻不卡视频| 久久五月综合| 五月婷婷六月丁香五月| 人妻五月天激情开心网| 玖玖午夜视频| 伊久大香蕉| 五月香婷婷| 色狠狠色综合| 五月婷六月综合在线观看| 九九九九成人| 国产真人做爰视频免费| 综合99久久天天综合| 97色伦另类图片小说视频| 亚洲熟女色| 久热re视频在线观看网站| 色欲色香综合网| 无码区婷婷五月花开| 五月丁香六月婷精品视频| 青青草色在线视频观看| 五月综合激情网| 亚洲色啪| 亚卅毛片| 五月婷婷六月丁香在线视频| 丁香六月 婷婷六月| 九九干视频| 亚洲AV成人一区二区在线观看| 婷婷六月啪啪| 国产精品国产| 五月草影视| 婷婷五月丁香高清无码| 精品久久9| 久久网址99热| 激情5月婷婷| 亚洲综合九九| 丁香五月综合婷婷| 久久久这里有精品| 婷婷激情网五月天| 天堂爱爱| WWW.天天日| 色呦呦美女| 日本三日本三级少妇三级66| 日韩国产在线精品| 色婷婷久久| 大香蕉手机视频| 六月撸婷婷| 五月天狠狠| 99国产小视频| 五月婷婷伊人久久| 欧洲亚洲最新精品| 男人的天堂999| 中文字幕在线不卡| 人橾人| 色婷婷文字幕| 色爱综合五月| 丁香婷婷久久综合在线| 欧美婷婷丁香社区在线播放| 粉嫩AV久久一区二区三区| www.五月天婷婷| 色九月| 免费AV播放| 久操热线| 成人AV在线电影| 成人在线精品| 九九综合| 五月婷婷综合激情网| 亚洲五月天婷婷| 色五月色图| 激情五月婷| 91碰在线| 99性视频| 伊人五月天在线| 五月婷婷在线免费观看| 99热这里只有精品1| 99免费| 思思99热| 欧美大片免费播放器| 色色无码| 江苏少妇性BBB搡BBB爽爽爽| 国产黄色在线| 天堂美国久久| 国产色色小草视频| 综合婷| 成人色图情色成人网 www.5b5b5bcom 五月天 | 五月在线| www.久久综合| 亚洲综合99| 五月婷婷综合视频| 六月婷婷综合| 日韩综合成人| 婷婷六月成人| 人人爽欧美婷婷久久久五月丁香| 无码yw| 大香蕉五月天| 七七九九色色| 99免费视频精品| 五月天激情无码专区| 色五月婷婷五月| 综合色五月天| 五月天激情网图片| 黑人巨粗进入警花疼哭A片| 97久久婷婷色| 九九综合图片网| 天堂资源欧日浪女在线播放| WWW.天天日| 久久婷婷五月综合色播| 久久婷婷激情视频| 色婷婷先锋| 网站免费一站二站| 熟女人妻视频| 久久天堂色| 中文字幕av久久爽一区| 色色影院aaaav| 色五开心五月五月深深爱| 99精品无码| 欧美精品18| 婷婷5月久久综合网站| 婷婷玖玖丁香| 五月丁香久人妻中文| A A色色| 色无码| 99色综合| 婷婷黄色网| 丁香九月久久| 亚洲性爱99在线| 性爱久久| 99∨VTV| 97人人操人人操人人操人人| 九九色婷婷| 五月情涩综合婷婷| 97碰| Av狠狠色丁香婷| 五月婷婷影| 亚洲五月天综合| 欧美色频| 99精品国产在热久久| 色婷婷色综合激情91| 99精品视频在线6| 五月天综合视频网| 性爱久久| 热婷婷久| 亚洲激情免费久久| 婷婷色导航| 噼里啪啦完整版中文在线观看| 亚洲五月天色色| 色七色九九| 婷婷五月在线观看| 少妇2做爰HD韩国电影| 人人插9| 三级成人网站| 婷婷精品在线| 老司机视频lsj爱就色| www.lingjunshare.com| 五月开心啪啪| 久热 91| 婷婷五月天淫荡| 深夜男女福利刺激影院一区| 九九热精品6| 日日干夜夜干| 欧洲精品爱爱| 噜噜噜精品欧美成人在线观看| 欧美色色色色色| 欧美三级大片AA在线看| 成人精品一区二区三区四区五区| 99热66| 琪琪色网在线| 五月色情婷婷| www.夜夜操| 五月天婷婷丁香成人网| 久久五月天色婷婷| 五月天天久久香| 影音 五月 婷婷 久久| 99亚色色色| 99综合自拍| 影院久久久| 国产美女精品| 六月综合婷婷开心伊人| www.五月丁香| 99无吗| 色吧婷婷五月亚洲| 黄色99视频| av色婷婷| 色色色色色五月| 色五月首页| 欧美色图片88| 中文人妻主播久久| se99视频| av色婷婷| 99九九玖玖| 激情国产综合| 五月婷婷丁香在线| 91色性感五月婷婷丁香| 99re这里只有精品视频6| 五月天婷婷视频| 玖玖在线视| 久久婷婷五月天| 色婷婷女优有码五月亭| 日日狠狠久久偷偷四色综合免费| 成人国产欧美大片一区| 激情综合五月激情| 婷婷五月天亚洲| 丁香婷婷性爱| 日本啪啪天堂| 日韩精品超碰在线观看| 玖玖婷婷五月天| 欧美日本97| 五月丁花六月丁香综合| 九月婷婷人人操人人舔人人爱| 婷婷五月免费在线| 一点色成人网| 色亭亭五月天网扯| 欧美性猛交 XXXX 乱大交| 99视频超级精品| 亚洲色婷婷五月天| 中文网AV| 91碰碰| 天天舔天天摸天天射| 这里只有精品视频在线| 色五月激情问网站| 一起草av| 婷婷深爱色五月| 香蕉97碰碰碰欧美| 久香草视频在线观看| 久久婷婷五月综合色丁香花| www狠狠| 久草婷| 26UUU精品一区二区| 99色| 91九色国产| 99热最新精品| 中字幕视频在线永久在线观看免费| 欧美色播综合在线观看| 国产看真人毛片爱做A片| 亚洲中文乱字字幕线在永久| 以及AA大片看看| 天堂草在线观看| 色色色9 9 9| 日韩久热| 激情五月综合色| 色婷婷成人做爰A片免费看网站| 五月丁婷婷| 2015在线中文字幕| 色色色色色网| 97操操操| 思思热在线观看| 婷婷丁香五月天亚洲| 久久五月天丁香| 五月丁香激| 一起草无码视频| 日韩AV中文在线观看| 色久五月天| 六月丁香六月婷婷欧美| 五月天婷婷在线AN| 五月婷人妻| 狠狠色综合久久久久| 五月99久久| 丁香五月情色| 日比网免费国产| 夜夜操天天干| 五月激情啪啪啪| 亚洲妇女熟BBW| se99视频| 热九九精品| 国外亚洲成AV人片在线观看| 俺也去在线视频 | 色在线视频网2025| 久热 91| 国产毛片欧美毛片久久久| 91精品久久久久久综合五月天| 婷婷爱爱蜜臀天天操| 激情五月天网站| 激情婷婷五月社区| 狠狠色综合网| 91麻豆国产三级精品福利在线观看| 欧美这里只有精品| 激情婷婷五月久久| 123日本不卡在线| 免费婷婷| 久久人人人人妻| 9福利性视频欧美| 久久AAAA片一区二区| 色色国产| 五月婷婷少妇之| 三级毛片7979| 天天躁日日躁狠狠躁日日躁2022年5月9日|