From 25352ba609e58a9f76f132a9686133b48d611918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E7=A5=A5=E4=BF=8A?= Date: Tue, 11 Jul 2023 10:40:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81xapk=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E7=9A=84apks=E6=96=87=E4=BB=B6=E8=A7=A3=E5=8E=8B-0706=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 3 +- .../com/gh/common/util/PackageInstaller.kt | 11 +- .../com/gh/common/xapk/XapkInstallReceiver.kt | 40 ++++-- .../java/com/gh/common/xapk/XapkInstaller.kt | 120 ++++++------------ .../gh/common/xapk/XapkPendingSessionInfo.kt | 21 +++ .../java/com/gh/download/PackageObserver.kt | 3 +- 6 files changed, 98 insertions(+), 100 deletions(-) create mode 100644 app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca02cb9529..609e51e83a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -830,8 +830,9 @@ - = Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT } else { - PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_CANCEL_CURRENT } - val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags) + val pendingIntent = PendingIntent.getActivity(context, sessionId, intent, flags) // 提交数据流并执行安装 session.commit(pendingIntent.intentSender) - session.close() } /** diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt b/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt index a9cea6e637..2f90ebb282 100644 --- a/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt +++ b/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt @@ -1,31 +1,35 @@ package com.gh.common.xapk -import android.content.BroadcastReceiver +import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageInstaller -import android.content.pm.PackageInstaller.SessionInfo import android.os.Build +import android.os.Bundle import androidx.annotation.RequiresApi @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -class XapkInstallReceiver : BroadcastReceiver() { +class XapkInstallReceiver : Activity() { companion object { const val KEY_PACKAGE_PATH = "package_path" } - override fun onReceive(context: Context, intent: Intent) { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + overridePendingTransition(0, 0) + handleIntent(this, intent) + + } + + private fun handleIntent(context: Context, intent: Intent) { when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { PackageInstaller.STATUS_PENDING_USER_ACTION -> { val installIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT) if (installIntent != null) { - val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH) - val installSessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) - XapkInstaller.onPendingUserAction(installPackagePath!!, installSessionId) - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - installIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) context.startActivity(installIntent) + updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_PENDING_USER_ACTION) + finish() } } PackageInstaller.STATUS_FAILURE, @@ -35,11 +39,21 @@ class XapkInstallReceiver : BroadcastReceiver() { PackageInstaller.STATUS_FAILURE_INCOMPATIBLE, PackageInstaller.STATUS_FAILURE_INVALID, PackageInstaller.STATUS_FAILURE_STORAGE -> { - val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH) - if (!installPackagePath.isNullOrEmpty()) { - XapkInstaller.onInstallCanceled(installPackagePath) - } + updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_INSTALL_CANCELED) + finish() + } + else -> { + updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS) + finish() } } } + + private fun updatePendingSessionInfoStatus(intent: Intent, status: Int) { + val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH) + if (!installPackagePath.isNullOrEmpty()) { + val pendingSessionInfo = XapkInstaller.getPendingSessionInfo(installPackagePath) + pendingSessionInfo?.updateStatus(status) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt index 149a015421..256eafa937 100644 --- a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt +++ b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt @@ -3,7 +3,6 @@ package com.gh.common.xapk import android.annotation.SuppressLint import android.content.Context import android.os.Build -import android.util.Log import com.gh.common.util.* import com.gh.download.DownloadDataHelper import com.gh.download.DownloadManager @@ -61,9 +60,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { private val mDownloadEntityMap = Collections.synchronizedMap(HashMap()) - private val mInstallingPath = Collections.synchronizedList(mutableListOf()) - - private val mPendingSessionIdMap = Collections.synchronizedMap(HashMap()) + private val mPendingSessionInfoMap = HashMap() // 按并行解压 @JvmStatic @@ -127,16 +124,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { Utils.log("unzip", "onSuccess->${downloadEntity.path}") } - if (!mInstallingPath.contains(downloadEntity.path)) { - mInstallingPath.add(downloadEntity.path) - } - - AppExecutor.ioExecutor.execute { - NDataChanger.notifyDataChanged(downloadEntity) - } - - XapkInstallerLooper.add(downloadEntity.path, installer) - XapkInstallerLooper.install(mContext) + installer.install(mContext) } override fun onError(apk: XApkFile, exception: Throwable) { @@ -180,11 +168,9 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { } } - fun isInstalling(packagePath: String): Boolean = mInstallingPath.contains(packagePath) + fun getPendingSessionInfo(packagePath: String): XapkPendingSessionInfo? = mPendingSessionInfoMap[packagePath] - fun onPendingUserAction(packagePath: String, sessionId: Int) { - mPendingSessionIdMap[packagePath] = sessionId - } + fun isInstalling(packagePath: String): Boolean = mPendingSessionInfoMap.containsKey(packagePath) /** * 通知XAPK安装完成 @@ -192,15 +178,11 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { fun onInstalled(packagePath: String) { val downloadEntity = mDownloadEntityMap.remove(packagePath) ?: return - mPendingSessionIdMap.remove(packagePath) - mInstallingPath.remove(packagePath) + mPendingSessionInfoMap.remove(packagePath) downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0" downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.INSTALLED.name - XapkInstallerLooper.remove(downloadEntity.path) - XapkInstallerLooper.install(mContext) - AppExecutor.ioExecutor.execute { NDataChanger.notifyDataChanged(downloadEntity) DownloadManager.getInstance().updateDownloadEntity(downloadEntity) @@ -212,16 +194,11 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { val downloadEntity = mDownloadEntityMap.remove(packagePath) ?: return - mPendingSessionIdMap.remove(packagePath) - - mInstallingPath.remove(packagePath) + mPendingSessionInfoMap.remove(packagePath) downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0" downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name - XapkInstallerLooper.remove(downloadEntity.path) - XapkInstallerLooper.install(mContext) - AppExecutor.ioExecutor.execute { NDataChanger.notifyDataChanged(downloadEntity) DownloadManager.getInstance().updateDownloadEntity(downloadEntity) @@ -234,31 +211,46 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { * 1. 用户触碰安装弹窗(原生Android系统)区域外导致安装取消 */ fun updateCurrentInstallStatus() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || mPendingSessionIdMap.isEmpty()) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || mPendingSessionInfoMap.isEmpty()) { return } - val installer = mContext.packageManager.packageInstaller + val updateList = mutableListOf() - for (pendingSessionEntry in mPendingSessionIdMap) { - val sessionInfo = installer.getSessionInfo(pendingSessionEntry.value) + for (pendingSessionInfoEntry in mPendingSessionInfoMap) { - // 1. 用户点击了取消按钮时,sessionInfo为空 - // 2. 用户点击了确定按钮时,sessionInfo的progress大于0.8 - if (sessionInfo == null || sessionInfo.progress > 0.8F) { - continue + val pendingSessionInfo = pendingSessionInfoEntry.value + + if (pendingSessionInfo.getStatus() == XapkPendingSessionInfo.STATUS_PENDING_USER_ACTION) {// 用户触摸安装弹窗外部区域取消安装后,更新安装状态 + val installer = mContext.packageManager.packageInstaller + val sessionId = pendingSessionInfo.sessionId + if (sessionId != -1) { + val sessionInfo = installer.getSessionInfo(sessionId) + // 表示用户点击了安装弹窗外部区域 + if (sessionInfo != null && sessionInfo.progress <= 0.8F) { + AppExecutor.ioExecutor.execute { + installer.abandonSession(sessionInfo.sessionId) + } + pendingSessionInfo.updateStatus(XapkPendingSessionInfo.STATUS_INSTALL_CANCELED) + } + } } - AppExecutor.ioExecutor.execute { - installer.abandonSession(sessionInfo.sessionId) + val installStatus = pendingSessionInfo.getStatus() + if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_CANCELED + || installStatus == XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS + ) { + updateList.add(pendingSessionInfo) } + } - val downloadEntity = mDownloadEntityMap[pendingSessionEntry.key] ?: return - - if (!PackageUtils.isInstalled(mContext, downloadEntity.packageName)) { - onInstallCanceled(downloadEntity.path) - } else { + for (pendingSessionInfo in updateList) { + val downloadEntity = mDownloadEntityMap[pendingSessionInfo.path] ?: continue + val installStatus = pendingSessionInfo.getStatus() + if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS) { onInstalled(downloadEntity.path) + } else if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_CANCELED) { + onInstallCanceled(downloadEntity.path) } } } @@ -284,10 +276,13 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { private val xApkFile: XApkFile, private val sessionId: Int, ) : IPackageInstaller { + override fun install(context: Context) { val downloadEntity = mDownloadEntityMap[xApkFile.file.path] ?: return val applicationContext = context.applicationContext + mPendingSessionInfoMap[downloadEntity.path] = XapkPendingSessionInfo(downloadEntity.path, sessionId) AppExecutor.ioExecutor.execute {// 有可能卡顿造成anr + NDataChanger.notifyDataChanged(downloadEntity) PackageInstaller.installMultiple(applicationContext, downloadEntity.path, sessionId) } } @@ -297,6 +292,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { private val xApkFile: XApkFile, private val file: File ) : IPackageInstaller { + override fun install(context: Context) { val downloadEntity = mDownloadEntityMap[xApkFile.file.path] ?: return PackageInstaller.install( @@ -307,42 +303,6 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory { ) } } - - /** - * XAPK安装每次只能执行一个安装流程,因此添加一个安装队列 - */ - private object XapkInstallerLooper { - - private val packagePathList = mutableListOf() - - private val installerList = mutableListOf() - - fun add(packagePath: String, installer: IPackageInstaller) { - if (packagePathList.contains(packagePath)) { - return - } - packagePathList.add(packagePath) - installerList.add(installer) - } - - fun install(context: Context) { - if (installerList.isEmpty()) { - return - } - installerList.first().install(context) - } - - fun remove(packagePath: String) { - if (!packagePathList.contains(packagePath)) { - return - } - val index = packagePathList.indexOf(packagePath) - if (index != -1) { - packagePathList.removeAt(index) - installerList.removeAt(index) - } - } - } } enum class XapkUnzipStatus(status: String) { diff --git a/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt b/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt new file mode 100644 index 0000000000..5815d9a0a6 --- /dev/null +++ b/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt @@ -0,0 +1,21 @@ +package com.gh.common.xapk + +class XapkPendingSessionInfo( + val path: String, + val sessionId: Int = -1 +) { + companion object { + const val STATUS_INIT = -1 + const val STATUS_PENDING_USER_ACTION = 0 + const val STATUS_INSTALL_SUCCESS = 1 + const val STATUS_INSTALL_CANCELED = 2 + } + + private var status = STATUS_INIT + + internal fun updateStatus(status: Int) { + this.status = status + } + + fun getStatus(): Int = status +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/download/PackageObserver.kt b/app/src/main/java/com/gh/download/PackageObserver.kt index 5be59056d2..6a1fbf05fc 100644 --- a/app/src/main/java/com/gh/download/PackageObserver.kt +++ b/app/src/main/java/com/gh/download/PackageObserver.kt @@ -111,8 +111,7 @@ object PackageObserver { runOnIoThread { FileUtils.deleteFile(mDownloadEntity.path) } } - if (mDownloadEntity.format == Constants.XAPK_FORMAT - || mDownloadEntity.format == Constants.XAPK_APKS_FORMAT) { + if (mDownloadEntity.format == Constants.XAPK_FORMAT) { XapkInstaller.onInstalled(mDownloadEntity.path) }