99精品久久精品一区二区-亚洲熟妇无码?v在线播放-日本国产精品无码字幕在线观看-久久久亚洲永夜AV-亚洲一级无码一区二区一-免费国产成高清人在线视频-中文字幕乱码免费观看-国产毛片精品妇女久久久

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
返回資訊列表
婷婷伊人综合| 91无码视频| 婷婷色狠狠| 伊人大香蕉综合在线| 9有码中文| 成人日韩欧美| www色婷婷com| 少妇高潮呻吟A片免费看软件| 天天天摸夜夜夜玩| sisi热国产| 啪啪操网| 久久婷综| 少妇AB又爽又紧无码网站| 五月天天天色| 婷婷丁香亚洲五月天| 九九偷拍网| 夜夜撸夜夜骑| 热久久91| 免费观看的av| 精品五月天| 丁香五月性| 丁香综合| 9色91视频| 五月天丁香婷婷社区| 五月天激情站| 、激情六月天| www.久久99| 午夜AV网| 丁香婷婷天堂| 最近中文字幕在线中文视频| 色五月丁香五月| 婷婷久久综合久色| 婷婷综合网在线| 人人操人人爰人人一天天碰夜夜拍夜夜爽-中国A级毛片天天看天天谢… | 99色| 九月丁香婷婷| 天天日天天色| 五月丁香六月激情在线| 久久99热这里只有精品| 九九热99热| 色五月丁香五月| 五月婷婷免费视频| 五月激情偷拍婷婷| 深爱激情五月天色婷婷| sS丁香五月婷婷| 1999天天操夜夜操| 十区AV| 熟妇人妻中文字幕无码老熟妇| 五月色综合| 成人片在线免费看| 日韩精品超碰在线观看| 久久99久久99精品免观看粉嫩| 国产97在线日韩亚洲女人被黑人巨大| 婷婷久久色五月婷婷久久久| 久久久国产精品黄毛片| 欧美三级巜人妻互换| 小视频久久久aaa| 国产avapp 网| 综合激情站| 激情综合五| 五月天网站免费欧美| 无月播播激情在线观看视频| 终合激情网| 婷婷五月天激情AV影院| 色色色色网| 六月丁香婷婷五月| 狠狠搞狠狠操| 99免费在线| 久久婷婷影院| 久久小说| 不卡成人免费| 91精品久久久久久久久 | 91久久1118| 久久久久亚洲AV无码网影音先锋| 九九aV| 99久在线精品| 狠狠狠狠免费| 狠狠xx| 性av| 五月婷婷激情综合| 91九色超碰| 天天做天天爱天天高潮| 国产午夜精品AV一区二区麻豆 | 国洲夜色亚热在线久久| 色五月婷婷av| 伊人天天色| 天天天天做夜夜夜夜做| 欧美大道不卡| 成人做爰高潮A片免费视频| av九九| 婷婷午夜| 五月丁香毛片| 亚洲精品操一操、噜一噜、摸一摸、爽| 99re这里只有精品免费| 无码操B| 99热这里只有精品9| 夜丁香综合| 色色五月天婷婷丁香| 超碰99久久| 色五月婷婷丁香五月| 五月婷婷六月丁香| 9色免费网| 9久久婷婷国产综合精品性色| 婷婷五月天美女| 日日鲁鲁夜夜爽爽| 五月丁香久久| 色婷婷四色| 九九热在线视频| 五月丁香| 百度4399有码精品V在线观看| 最新日韩久热免费视频看看| 婷婷色无码| 五月婷婷丁香色吧网| 99爱在线视频| 影音先锋男人站,影音先锋男人色资源网,影音先锋AV最新资源站,影音先锋AV资源 | 久久五月天婷婷| 九九热这里有精品视频| 婷婷色五月综合| 五月丁香婷婷人体| 婷婷激情六月综合| 国产av一区二区三区| 九九精品99久久久| 六月婷婷色宗合| 99国产精品久久久久久久久久久| 97爱艹婷婷开心丁香激情综合| 性爱五月婷| 99九九精品| 久久久久久综合五月婷婷| 99九九视频| WWW.国产| 五月香蕉综合| 激情综合网激情五月天| 精品人人操| www.狠狠干com| 婷婷五月丁综合| 九九热视频精品| 五月婷A V在线| 色婷婷五月综合| 婷婷.com| 婷婷五月激情综合网| 超碰成人免费| 色综合九九| 久久五月综合| 久久密臀婷婷| 欧洲高清免费久久| 综合天堂AV久久久久久久| 九九热10| 9 1超碰九色| 久久综合香蕉国产国产蜜臀AV| 七七九九色色| 色婷婷免费视频| 99热在线极品极品| 这里只有精彩视| 99色热| 7777久久亚洲中文字幕| 天天爱夜夜爽| 人人搡人人| 99热九九在线| 五月色丁香婷婷综合| 激情五月网站| 成人.在线日韩| 五月天婷婷三级黄| 国产成人精品一区二三区熟女在线| WWW.婷婷五月天.COM| 久久成人人妻| ′久久99一| 亚洲欧美国产高清vA在线播放| 三级av在线| 欧美成人网婷婷综合在线| www.国产色| 亚洲av网站| 五月天天视频| 美女网黄| 少妇做爰免费视看片| 人妻射精AV| 久久久网站| 色欲天天综合网| 亚洲成人综合在线| 色婷婷黄色网络| 亚洲不卡| 天天色综合色色色色色。| 日韩AV大全| 亚洲超碰在线| 99热在线观看| 日本色五月| 婷婷久久18| 五月婷婷综合丁香视频| 玖玖资源站国产| 九九無妻| 丁香五月成人在线| www.综合久久.com| 国产在线aaa片一区二区99| xxxx五月| 啪啪五月综合| 丁香五月在线| 99免费视频网| 99视频内射三四| 五月婷婷丁香| 蜜臀99精品| 色播婷婷五月天| 狠狠操综合| 六月丁香射婷婷欧美色图片| www久久久久| 九九热免费| 婷婷色五月亚洲| 色欲久久99精品久久久久久| 日本久久人| 色综合色五月| 中文字幕91,综合| 丁香五月天电影| 草AV9999| 欧美日韩99| 无码AV免费精品一区二区三区| 《》【无码】想被搞到爽AV应募而来的超M素人 西纯子 10musume-011723-01 | 日本精品。999| .肏屄视频一区二区| 开心五月综合| aaa丁香五月天| 婷婷情色五月| 99啪啪| 啄木鸟丝袜美女福利视频| 婷婷丁香97| 国产成人AV| 久久五月天网| 夜夜夜叫天天天做| 丁香五月综合久久| 人妻五月天激情开心网| 天天综合天天玩夜夜玩天天玩夜夜玩| 成人网在线视频| 国产激情综合| 亚洲五月婷婷| 九月综合| 婷婷五月综合激情免费| 久久五月天合网| 伊人久久综合| 色五月色情| 偷偷与邻居做爰完整视频| 久久a热| 婷婷五月激情四月综合 | 天天日天天色| 久久伊人五月天| 婷婷六月五月天综合| 1024日韩| 噜噜狠狠色综合久| 五月色丁香婷婷综合| 九九黄色网| 五月天激情网图片| 26uuu在线观看| 五月激情日本在线| 91偷拍视频| 五月丁香趴趴| 综合激情肏逼网| 婷婷综合在线网| 国产成人精品一区二三区熟女在线| 五月丁香操婷逼| 岛国av电影网站| xxxx五月| 艹| 99色色最新视频| 亚洲综合激情五月久久| 久久五月天精品视频| 久大香蕉| 六月激情婷婷| 欧美在线视频99| 国产成人网站在线观看| 激情九九六月激情免费视频| 亚洲AV成人精品网站在线播放| 99精品视频在线观看| 性爱视频99| 亚洲99综合| 九热精品| 色综合色色| 色五月丁香婷婷| 九九99免费视频| 五月天激情小说网| 九九热婷婷| 婷婷五月天色播| 三区激情四射av| 中文久久久人妻| 色婷婷久久综合久色| 婷婷五月综合婷婷| 六月婷婷七月丁香| 激情综合网五月| 丁香五月天资源网| 人妻久久久久久久 | 丁香婷婷综合五月天| 丁香五月婷婷婷婷欧美综合| 久久这里只有精品网| 六月丁香婷婷天堂| 天天激情| 色婷视频| 丁香婷婷九月在线| 成人在线综合| 久久五月天合网| 激情5月婷婷狠狠干| 日本在线免费中文com.| 深爱激情婷| 99狠狠操一| 99视频内射三四| www.99热| 五月丁香花激情啪啪网| 五月天大香蕉| 深爱五月婷| 婷婷五月天美女21p| 91viP在线看| 婷婷色导航| 久久伊人大香蕉| 日韩操人| 性爱网六月丁香| 丁香五月人妻| 99re热视频| 日日干日日| 激情五月天社区| 欧美大肥婆大肥BBBBB| 久婷狼色诱惑在线| 九九Av| 亚洲 小说 欧美 激情 另类| 伊人玖玖精品| 91干婷婷| 性色欲情 网站| 亚洲 五月 婷婷 成人| 五月丁香六月| AV中文网| 欧美久草在线日本一级特黄大片做受9在线观看韩国电影《两个女人》未删减-毛片 | 日日撸夜夜操| 91九九九九| 性热视频99精品| 玖玖资源站国产| 五月天啪啪| 丁香五月电影| 丁香婷婷91在线观看视频| 五月丁香六月婷婷,婷| 操一操插一插| 亚洲视频一区| 五月婷婷色播视频| 超碰在线精品| 欧美伊人9| 99色这里| 夜夜资源站| 涩丁香| 亚洲人妻av| 丁香六月色婷婷| 9久久婷婷国产综合精品性色| 日韩免费视频| 99免费热在线精品| 激情综合五月| 色综合网址| 亚洲综合丁香五月天| 天天爽天天操| a色色色色色| 久久这里只有精品热在99| 欧美丰满熟妇BBB久久久| 六月婷婷色色色| 国产色99| 69人人操人人爽| 日日想日日夜日日操| 婷婷色五月天在线| 激情网第九色| 婷婷丁香五月亚洲17cao| 国产婷婷久久| 六月天婷婷| 欧美色偷偷大香| 五月丁香成人| 丁香五月五月婷婷欧美大香蕉| 色色五月婷婷| 91一起操| 婷婷五月天无码视频| 99精品久久| 五月天婷婷免费| 色色色色色色网| 偷偷操九九| 人人操Av| 五月开心播播网| 丁香婷婷精品视频| 五月天激情国产综合婷婷婷 | 97在线视频观看| 99在线视频播放| 夜夜操狠狠操| 成人婷婷| 97狠狠色| 久久久久久久久久久久久久人妻视频 | 丁香六月爱综合| 综合AV网| 色综合激情| 五月天婷婷激情四射综合| 伊人五月成人| 中文字幕,综合,91| 天天骑天天操| 激情五月天之五月婷婷| 99色色网| 三十路磁力链接| 亚洲午夜成人av电影网| 九九久久五月天综合伊人| 激情涩播| 99人妻碰碰碰久久久久视| 色欧美影院| 五月丁香成人| 精品网站99| 大色鬼综合| 色5月婷婷| 久久婷婷东京热大香樵| 色九月婷婷| 九九久久五月天综合伊人| 激情综合久久| 激情 婷婷 丁香五月天| 99在线热视频| 嫩草AV久久伊人妇女超级A| 激情网狠狠干| 激情九月天天天天婷婷| 五月激情视频| 天天五月天综合网址| 91综合网| 五月婷婷丁香五月| 99在热线免费视频| 这里只有精品99视频| 日韩AC在线免费观看| 久久色五月天激情小说| 久久综合婷婷| 六月婷婷操逼| 激情99| 色综合久久久久久久久五月| 婷婷激情九月| 丁香五月综合狠狠| 日本99久久| 天天拍夜夜爽| 97久久人人| 成熟妇人A片免费看网站| 激情综合网五月激情| 五月天色色网站| 五月丁香色欲| 丁香五月影院| 99精品综合| 日日夜夜青青草| 日韩精品超碰在线观看| 熟女色色一区二区| www久| 五月天久久91| 另类综合国产| 任你草| 天天射天天插天天干| 大香蕉99热| WWW五月婷婷| Www.狠狠| 超碰自拍天堂| 激情五月四色| 97超碰婷婷五月天| 97色色色色色| 香蕉综合在线| 97久久人人| 婷婷五月性感| 丁香六月婷婷综合| 五月天六月色| 大香蕉久| 99热播放| 五月丁香综合激情网| 五月婷婷视频28| 6月丁香婷婷| 国产小网站| 中国丰满熟女A片免费观| 99九无网码| 五月激情婷婷六月| 深爱激情网五月天| 丁香婷婷色五月天| 色婷婷影音| 大香蕉视频婷婷| 五月婷婷激情网| 中文字幕成人| 久热这里| 91色婷婷综合久久中文字幕二区| 亚洲成人在线播放| 操碰99| 黄网在线观看免费| 久久免费操| 97干综合网| 天天色色天天| 色综合激情| 久久精品亚洲一级牲爱综合| 青青草原精品久久| 亚洲欧美一区二区三区四区爱爱动图| 色色丁香激情五月| 第四色首页| 九九精品视频在线6| 丁香五月六月久久综合| 狠狠 婷婷| 激情五月伊人婷婷| 99亚洲大片精品永久在线观看| 激情丁香淫荡婷婷| 99热只有这里才是精品| 激情五月丁香综合网站| 激情婷婷六月天| 欧美丁香五月夫妻天| 永久精品| 一起草av| 狠狠CAO日日穞夜夜穞AV| 嫩草AV久久伊人妇女超级A| 夜夜夜叫天天天做| 亚洲国产va| 亚洲另类噜噜| 亚洲色激情| 人人草人人舔| 亚州操人在线视频| 无码成人播放器| 猫咪伊人AV| 超碰中文字幕在线| 伊人久久艹| 色五月婷婷婷婷婷婷婷婷婷婷| 色婷婷婷av| 26uuu欧美亚洲日韩| 久热只有这里精品| 99九无网码| 激情五月婷婷五月| 丁香激情六月天婷婷| 日日爽天天| 五月天激情视频| 婷婷激情五月综合| 色五月亚洲| 九九精品丁香花| 色情五月天。| 人人操Av| 97色啪| 色偷偷综合| 99久久精品免费精品国产_国产精品久久久久久_国产在线|日韩_久久国产精品电影 | 九九热99re8热免费观看| 狠狠狠激情网| 天天爱天天狠天天透| 国产激情在线| 无码激情AAAAA片-区区| 久久99网址| 玖玖伦理电影| 99re热在线视频观看| 色色五月天婷婷| 五月天伊人久久| 日韩婷婷五月| 丁香五月伊人| 亚洲性爱AV在线| 狠狠干综合网| 色婷婷狠狠干芒果TV| 可以免费观看的AV| 精品国产乱码久久久久久免费| 任你干嘛免费视频播放| 中文aV网| 五月丁香六月色| 涩婷婷视频快播人妻| 久久五月天色婷婷| 99热精品在线免费观看| 最近韩国日本免费高清观看| 91视频一起草| 日日综合网| 狠狠狠狠狠狠| AV在线观看网站| 成人性爱无码| 少妇搡BBBB搡BBB搡毛茸茸| 9精品视频在线观看| 成年人99热| 色九网| 五月情涩综合婷婷| 五月丁香在线偷拍视频| 久热免费视频| 99精品久久| 激情久久丁香| 激情婷婷五月综合| 九九热视频思思| 色婷婷五月天| 免费的日逼视频| 久久久激情视频| 婷婷影视久久| 日本色婷婷| 九九精品热播| caopeng97人人| 久久人妻人人槡| 国产精品第一国产精品| 激情网第四色| 国产午夜精品一区二区三区四区| 久这里只有精品99| se99视频| 色色色777| 这里只有精品视频99| 99久久.www| 五月天婷婷激情综合| 99久久97| 天天日天天干天天天| 婷婷香蕉精品| 99热在线观看这里只有精品| 九九热这里只有精品9| 亚洲丁香五月深爱五月| 96精品成人无码A片观看金桔| 婷婷丁香六月五月天| 五月婷婷天天| www..999热久| 色色色婷婷五月天| 性五月激情| 亚洲综合在线视频| 色五月婷婷久久爱| 97在线观看| 亚洲综合五月天综合| 亚洲精品无人区| 玖操97| 九九热狼人| 特级操b片| 夜夜干夜夜操| 五月色丁香| 激情文学久久| 亚洲精品乱码久久久久99| 久久久久久欧美精品se一二三四| 六月婷伊人| 五月丁香亭亭电影久久| 五月天婷婷婷| 亚洲综合婷婷五月| 成人无码髙潮喷水A片| 亚洲小说欧美激情| 久热精品在看| 丁香五月天啪啪| 五月丁香天天| 九九人人看| 久久 天天| 综合久久高清| 嫩草AV久久伊人妇女超级A| 婷婷香蕉视频| 97人人干| 一本久久亚洲五月婷婷| 六月婷婷AV| 五月婷婷久久综合| 婷婷五月天性| WWW99热| 欧美成人精品A片免费一区99| 99热精品9| 99久操| 777精品久无码人妻蜜桃| 日本在线噜噜| 久久综合五月天| 亚洲爆乳无码精品AAA片蜜桃| 天天草天天日| 婷婷五月天成人| 色九四色| 久久精品婷婷| 婷婷激情鹿城五月天| 中文字幕+中文在线| 激情五月天婷婷| 金桔一区二区ab地址| 婷婷开心激情| 热婷婷久| 综合婷婷六月| 婷婷五月天激情影片| 色五月综合| 婷婷五月色| 婷婷久久亚洲| 性色播| 亚洲天堂九九九| 亚洲综合五月天| 天天色综合网吨吧| 欧美狠狠草| 9视频1在线| 97超碰免费超级在线观看| 久99热在线观看| 久久在线人妻| 丁香五月天激情五月天激情五月天激情网| 久婷自拍视频| 激情涩播| 操操啪| 久久人人人人妻| 亚洲激情AV| 一区二区aV电影免费看| www婷婷色| 99ri在线视频| 色综合婷婷| 亚州色色色| 色综合五月在线| www.天天色综合| 97干在线播放| 1024成人在线观看| 9久久网| 成人国产欧美大片一区| 九九在线视频| 丁香五月成人网| 五月丁香六月成人| 伊人在线视频| 激情五月天婷婷丁香 | 色在线免费观看| 国产AV一区二区三区最新精品| 婷婷综合网| 久久玖玖综合| 超碰97免费在线| 五月激情婷婷在线| 色五月丁香婷婷在线观看| 五月天色婷婷基地| 337午夜福利| 五月综合六月丁| www.91.com处女在线直播| 91久久久久久| 草综合网| 五月婷免费视频久久久| 婷婷五月丁香高清无码| 26uuu亚洲欧美另类| 久久视频这里都是精品| 夜夜做天天爽| 五月丁香花开综合网| 天天操,夜夜骑| 亚洲情a| 男人的天堂五月丁香| 九九精品视频在线观看| 天天摸天天舔| 國語久久婷| 91碰碰| 亚洲综合色网| 欧美天堂婷婷日韩| 丁香五月婷婷图片综合| 99久久婷婷综合| 一区二区你懂的| 久久日韩婷婷五月| 色婷婷久久天天性爱| 久99久视频免费观看| 日本成人综合| 爽极品色| 色综合色综合色综合高潮| 亚洲第一色色色色| 五月天久久综合婷婷丁香| 综合网激情| 女同激情久久av久久| 天天骑天天操| 狠狠久久婷五月综合色| 啪啪色区| 亚洲精品无码久久| 亚洲AAAA网| www.五月天婷婷| www.99热这里只有精品| 欧美性色A片免费免费观看的 | 99久久99九九99九九九| 99精品激情| 九六五月天婷婷| 色五月AV| 日韩成人精品中文字幕| 五月婷丁香久久综合| 亚洲综合婷婷五月| 久久午夜理论| 操操自拍| 激情五月丁香五月色| 国产午夜精品一区二区| 天天成人综合| 色婷婷五月天成人网| 婷婷五月激情综合| 国产AV一区二区三区最新精品 | 日本三级大片| 日韩精品二三区| 五月色色网| 欧美搡BBBBB摔BBBBB| 91久久九色| 夜夜骑日日操| 午夜婷婷久久| 综合色图婷婷| 九九99香蕉在线视频播放| 伊人大香五月天| 美欧成人视频| 国产精品国产成人国产三级| 五月天色色婷婷| 婷婷五月天激情综合深爱| 综合性爱网| 婷婷五月丁香综合亚洲| 99操碰| 99久久综合精品五月天| 99热6精品| 中文字幕网站在线观看| 亚洲色色色色| a网站免费观看| 色婷婷888| 综合色五月| 亚洲字幕AV一区二区三区四区| 猫咪伊人久久| 久久九九热视频| 99热这里有精品| 超碰91在线| 婷婷五月综合啪| 色婷婷av综合网| 五丁香激情综合| 色吧五月婷婷| 2017人人操| 丁香五月激情澎湃一区| 色一情一乱一乱一区91Av| 五月天天堂久久| 婷婷丁香五月天狠狠| 亚洲激情在线| 色狠狠五月天| 九九热在线99| 中文婷婷狠狠| 五月开心激情网| 色婷婷影院| 九九九这里只有精品| 人妻九九九九| 超碰69天堂| 日韩精品色| 亚洲视频一区| 秋霞簧片| 玖玖爱伊人网| 国产色五月婷婷| 色五月婷婷777| 九九这里只这里只有精品| 夜夜撸天天操| 五月丁香六月色婷| 色婷婷激情| 伊人网碰碰| 五月婷婷色影院| 欧美日韩123| 成年人丁香五月| 99热只有精品在线| 丁香婷婷九月| 婷婷色在线视频| 国产成人av在线播放| 97热这里精品在线视频| 久久久久9| 丁香综合婷婷开心激情网| 久久婷狠狠色| 久热久色| 风流少妇A片一区二区蜜桃| 丁香伊人综合| 九九色婷婷Av| 亚洲情欲| 亚洲区1| 五月婷婷在线观看| 欧美日韩中国| 激情AV| 婷婷五月天天天日日夜夜| 五月丁香六月欧美综合网站| 国产色色网站网址| 五月婷A V在线| 99九九精品| 超碰在线观看9| 超碰免费99| 九九热视频首页/这里只有精品| 99国产精品白浆在线观看免费| 三十路磁力链接| 婷婷五月开心六月AV| 97干综合网| 久久婷婷六月| 婷婷六久久| 久久婷婷五月综合伊人| 琪琪秋霞| 丁香五月www| 99热自拍| 婷婷五月天激情AV影院| 综合99久久天天综合| 天天弄天天操| 婷婷六月激情综合| 五月天大香蕉| 欧美三级A做爰在线观看| 丁香五月天激情AV| 久久99精品久久久久久青青AR| 天天肏在线观看| 五丁香激情综合| 影音先锋91资源站| 韩国真做片在线观看| 丁香五月激情视频| 大香蕉婷婷五月| 欧美激情综合五月色丁香| 婷婷涩涩五月天| 538在线精品| 五月丁香影视| 岛国资源网| 婷婷五月色影视先锋| 乱岳熟女50岁| 人妻久久婷婷| 爱婷婷五月| 伊人久久艹| 热久免费视频9| 婷婷激情综合| 99这里热| 五月婷综合网| 九九热这里只有精品556| 日韩欧美性爱| 国产亚洲精品久久久久久豆腐| 五月婷视频在线| 五月丁香好婷婷A片网| 人伦30P| 日本色超碰| AV在线观看网站| 婷婷五月天综合色| 26uuu四色| 97在线/亚洲| 欧美三级大片AA在线看| 少妇高潮A片无套内谢麻豆传| 99久在线精品| 天天骑天天操| 99亚洲色| 六月丁香婷啪射| 九九黄色网| 国产九月婷婷| 免费超碰在线观看| 日本色婷婷| 九九综合| 色婷精品91| 天天爽天天干| 99爱在线精品视频免费观看| 国产一级婬片毛片| 狠狠夜夜五月丁香| 丁香婷婷激情综合五月激情| 五月久久网| 婷婷99狠狠躁天天久久久九九九| 色色国产| 99色色网| 亚洲色频| 欧美内射AAAAAAXXXXX| 色五月xxx| 99ri精品| 婷婷综合色色| 成人在线精品| 日韩性视频| 中美日韩成人在线| 日韩狠狠色| 国产老熟妇亲子乱对白| 色吧99| 国内精品99| 亚洲无码影音| 五月婷无码| 亚洲成人精品三区| 99丝袜精品视频网站| 久久99网| 九九精品少妇| 日韩三及成人AV片| 婷婷五月丁香超碰| 久久久久久久久久91| 人人综合久| 日本三级日本三级99| 日都一级A片| 国产在线视频1234| 99爱爱网| 午夜色婷婷| 九九综合视频在线观看| 激情com| 亚洲欧洲自拍图片专区五月天| 婷婷五月天影视| 五月天电影网| 色99在线观看| av网址在线| 久久婷婷热| 九九色逼| 碰超亚洲| 九月婷婷综合网| 欧美人人操| 激情婷婷内射| 中出内射的人妻视频| 99性爱视频网站| 综合婷婷五月天| 操人91| 国自产拍偷拍精品啪啪一区二区| 丁香8月手机综合| 色九九中文字幕| 婷婷刺激综合| 大香蕉久久| 亚洲精品久久久久久久久久吃药| 99热国产这里只有精品| 婷婷激情性爱| 天天操天天插| 9在线9在线婷婷在线国产| 五月天婷婷网站888| sesesesezonghe| 国产色五月婷婷| 99色在线观看视频| 五月丁香成人网| 欧美日韩国产成人在线| 日韩操人| 丁香婷婷色五月天| 一区视频网站| 国产精品第一国产精品| av线电影| 丁香六月色香蕉视频| 五月情婷婷| 五月丁香人妻| 97成人视频| 色五月天.con| 岛国AV网站| 五月丁香影视| 玖玖玖婷婷婷| 好吊操这里只有精品| 伊人五月天在线| 色99视频| 久久精品天| 久热2025无码| 婷婷婷婷婷婷婷婷| 欧类av怡春院| 久九色| 狠狠搞亚洲| 婷婷五月天激情网| 亚洲操B视频| 三十路磁力链接| 91婷婷色 | 婷婷五月天影院| 色九月婷婷丁香| 国产成人va在线| 五月激情婷婷在线| 婷婷五月18永久免费网站| 激情五月天之六月婷婷| 婷婷丁香成人| 91精品啪| caobi四区| 99热只有| 五月丁香婷婷狠狠操| 色一区高清| 丁香六月狠狠干| 婷婷色狠狠| 丁香五月婷婷六月婷| 亚洲精品成人| 五月婷婷六月综合| 欧美英丁香开心快乐六月天网| 婷婷五月情| 激情五月综合久久| 色色色色色网站| 国产精产国品一二三在观看| 99爱视频精品在线观看| 丁香五月婷婷AV| 色情五月| 久久AV电影| 极品人妻VIDEOSSS人妻| 丁香六月天AV| 九九热最新视频| 国产 亚洲 在线| 99九九视频| 百度4399有码精品V在线观看| 五月婷婷我| 亚洲国产精品二二三三区| 日日爱678| 婷婷五月天AV| 操碰99| 欧洲不卡视频| 人伦30P| 久久伦乱| 国产精品视频免费看| 天天插AV丝袜中| 色九月丁香婷婷蜜桃在线观看| 丁香五月婷婷天激情| 欧美叉叉叉BBB网站| 天天综合插插| 中文字幕无码人妻少妇免费视频 | 色久婷婷网| 激情婷婷| 开心五月婷婷六月丁香| 欧美99| 激情五月综亚网| 日本精品人妻无码77777| 久久久久综合激动五月天| 五月天开心网| 五月天成人网婷婷| 国产麻豆视频| 思思久久青草热| 日韩婷久| AV成人在线播放| 青青草tp| 999热这里只有精品| 色之综合网| 97 天堂| 久久成人人妻| 超级碰碰碰碰视频| 特黄三级片| 五月噜噜噜色综合| 色婷婷网大全在线| 成人免费va| 色综合中文| 欧洲色| 天天开心AV色综合婷婷五月天| 久久免费少妇高潮99精品| www激情婷婷com| www.夜夜爱.com| 99性爱视频| 婷婷色五月激情| 午夜69成人做爰视频| 狠色色狠网| 欧美日本黄色| 91超级碰| 天天天天天久久久久久| 天堂综合久| 丁香五月天之婷婷影院| 婷婷中文字幕版| 卡视频1区2区| 深爱激情六月天| 99爱这里只有精品| 五月婷婷黄网站大全| 少妇性按摩无码中文A片| 九九精品在线视频观看| 丁香综合日产精品久久| 这里只有精品免费观看网占| 日韩av在线免费观看| 久久XX| 免费不卡狠操美女视频网 | 九九热婷婷| 思思视频这里是精品| 中文字幕丰满乱孑伦无码专区| 大香线蕉伊人| 婷婷五月激情片| 久9热在线免费观看| 激情视频网址| 99愛国产| 九九精品在线视频观看| ai97re99一本| www.91在线观看| 丁香五月天婷婷久久综合| 99视频91| 内射在线CHINESE| 99九九玖玖| 婷婷丁香五另类网站| 久婷婷五月天影院| 碰97 久| 五月丁香成人| 亚洲bt丁香五月天婷婷激情小说| 久久综合婷婷五月| 成人超碰网| 久99| 狠狠高潮精品亚洲1| 成人婷婷桔色| 色婷婷影视99| 99精品视频在线观看| 婷婷综合| 在线网黄| 五月丁香在线| 色 色 色综合com| 色噜综| 天天色伊人| 99激情网| 久久精品99| 九九综合伊人| 超碰色色综合| 久热九九| 九九色99| 婷婷性爱网| 欧美婷婷综合| 久久婷婷色五月| 91丨熟女丨首页| 97碰碰人人| 久久在线大香蕉| 综合久久五月| 狠狠插.com| 婷婷成人五月天| 五月天色色色| 五月天丁香婷| 六月激情网| 亚洲欧美一区二区三区爱爱动图| 色婷婷五月天激情综合| 《诡秘之主》在线观看| 超碰人人艹| 91丁香五月| 五月天婷婷色色网| 五月天怕怕| 狠狠干狠狠操狠狠爱| 91在线就要啪| 色欲一区二区三区精品A片| 在线播放成人网站| 五月丁香婷婷欧美| 99亚州综合精品成人网| 色~性~乱~伦~噜| 久久婷婷综合五月天| 夜丁香五月婷婷| 久久人妻高清中文| 亚洲综合婷婷五月天| 五月丁六月香av| xxxx五月激情| 丁香五月综合久久八| 热99精品视频在线观看| 丁香五月综合高清在线| 思思99热这里只有精品| 五月丁香啪啪激情| 99碰碰中文| 91 原创 在线 九色| 97色啪| 97人人操在线| 久久黄A片| 久热 91| 97人妻碰碰碰久| 久久草人妻| 色综合色综合色综合高潮| 亚洲热综合网在线观看| 99黄色| 色偷偷色婷婷| 婷婷五月天天爽| 热久久91| 色综合婷婷| 天天天天天天操| 国产超碰在线| 婷婷五月激情四射手| 五月天婷婷色综合| 婷婷终合色图| 操比激情五月| 日本一毛片| 丁香五月天无码AV| 丁香花五月天| 九九色热视频| 久久九区| 9国产在线视频| 狠狠色噜噜狠狠| 久婷婷五月丁香在线观看| 丁香五月综合网亚洲综合欧美狠狠| 射区导航| 777影视理论片大全在线观看| 五月婷天堂视频| 婷婷五月天丁香花| 激情六月一二| 天天插综合| 一区操| 婷婷五月天激情文学小说| 九九亚洲视频| 极品人妻VideOssS人妻| 久久久久九九九九视屏小说88| 双性美人被调教到喷水A片| 丁香五月婷婷欧美成人色图| 99视频色在线观看| 丁香久久| 一起草AV| 91超碰在线播放| 五区毛片七区毛片| 婷婷99| 激情文学 综合 九月| 台湾综合丁香五月蜜桃| 天天操天天操天天操天天操天天操| 六月婷婷国产| 天天操九九插| 99在线精品视频| 4399无码视频二区| 亚洲色婷婷| 亚洲中文乱字字幕在线永久| www.五月天| 亚洲无码播放| 天天干天天色综合| 四川BBB搡BBB爽爽视频| 丁香五月欧美激情| www,99热| 99ER热精品视频| 六月色婷婷色| 久久婷婷五月天激情四射| 91呦呦呦| 五月婷婷开心色伊人| 亚洲天堂爱爱| 99日在线观看视频| 综合五月激情| 精品一二三区久久AAA片| 日本三级日本三级三级人妇四虎| 深爱激情五月婷婷| 丁香婷婷五月香蕉91| 五月丁香日本在线视频观看| 超碰在线免费9| 久碰久| 丁香五月天BBw| 国产露脸150部国语对白| 亚洲色婷婷| 婷婷射综合| 91九色超碰正在播放| www激情五月天| 99色人| 婷婷五月综合在线视频| 91蜜桃婷婷狠狠久久综合9色| 亚洲综合网 665566| 日亚二欧美| 97超级碰碰碰| 人人干av| 亚洲欧美国产A片免费观看| 99久久大片| 香港九九六区八区99| 五月天婷婷影院影院| 97精品人人A片免费看| 丁香激情合作五月| 性做久久久久久久免费看 | 嫩草乱码一区三区四区| 激情婷婷激情在线不卡| wwwxxx五月婷婷小说| 男同91 | 久久视频婷婷视频| 婷婷五月天激情小说| 五月综合丁香婷婷| 五月婷中文字幕| 婷婷五月av| 五月天激情影院| 久色成人|