fix: 移除部分在 DownloadObserver 进行的数据库操作

This commit is contained in:
chenjuntao
2023-07-05 18:11:22 +08:00
parent 0e9f7feff9
commit 9472cafe48
9 changed files with 139 additions and 65 deletions

View File

@ -20,6 +20,7 @@
-keep class com.gh.gamecenter.db.info.* {*;}
-keep class com.gh.gamecenter.entity.** {<fields>;}
-keep class com.gh.gamecenter.qa.entity.** {<fields>;}
-keep class com.gh.download.DownloadDataSimpleEntity {<fields>;}
-keep class com.gh.gamecenter.floatingwindow.FloatingWindowEntity {<fields>;}
-keep class com.gh.gamecenter.BR
-keep class com.gh.gamecenter.retrofit.* {*;}

View File

@ -39,8 +39,11 @@ object DownloadObserver {
// TODO 修复因为更改内存对象造成的双重下载完成事件问题,具体触发代码见 DownloadDao.updateSnapshotList
private var mDoneDebouncePair: Pair<String, Long>? = null
private const val TAG = "DownloadObserver"
private const val CORE_EVENT_DOWNLOAD_COMPLETE_LOGGED = "CORE_EVENT_DOWNLOAD_COMPLETE_LOGGED"
private val mRetryableHashMap = hashMapOf<String, Boolean>()
// 如果在WIFI状态下,下载自动暂停,则再重试一遍
@JvmStatic
fun initObserver() {
@ -70,28 +73,34 @@ object DownloadObserver {
val currentActivity = AppManager.getInstance().currentActivity() ?: return
DialogHelper.showDialog(currentActivity, "下载失败", "下载链接已失效,建议提交反馈", "立即反馈", "取消", {
HelpAndFeedbackBridge.startSuggestionActivity(
currentActivity,
SuggestType.gameQuestion, "notfound",
StringUtils.buildString(downloadEntity.name, ",问题反馈:下载链接失效"),
SimpleGameEntity(gameId, downloadEntity.name, "")
)
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
DialogHelper.showDialog(
currentActivity,
"下载失败",
"下载链接已失效,建议提交反馈",
"立即反馈",
"取消",
{
HelpAndFeedbackBridge.startSuggestionActivity(
currentActivity,
SuggestType.gameQuestion, "notfound",
StringUtils.buildString(downloadEntity.name, ",问题反馈:下载链接失效"),
SimpleGameEntity(gameId, downloadEntity.name, "")
)
},
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return
} else if (DownloadStatus.neterror == downloadEntity.status
|| DownloadStatus.timeout == downloadEntity.status
|| DownloadStatus.diskioerror == downloadEntity.status
|| DownloadStatus.diskisfull == downloadEntity.status) {
if (downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD].isNullOrEmpty()
|| DownloadStatus.diskisfull == downloadEntity.status
) {
if (mRetryableHashMap[downloadEntity.url] == true
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)
) {
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = downloadEntity.progress.toString()
downloadManager.updateDownloadEntity(downloadEntity)
downloadManager.resumeDownload(downloadEntity.url)
debugOnly {
Utils.log("DownloadObserver", "下载重试->" + downloadEntity.toJson())
}
mRetryableHashMap[downloadEntity.url] = false
Utils.log(TAG, "下载重试->" + downloadEntity.toJson())
} else {
if (DownloadStatus.diskisfull == downloadEntity.status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
@ -102,13 +111,10 @@ object DownloadObserver {
}
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
debugOnly {
Utils.log("DownloadObserver", "下载自动暂停->" + downloadEntity.toJson())
}
Utils.log(TAG, "下载自动暂停->" + downloadEntity.toJson())
}
} else if (DownloadStatus.redirected == downloadEntity.status) {
debugOnly { Utils.log("重定向完毕") }
Utils.log(TAG, "重定向完毕")
DownloadDataHelper.uploadRedirectEvent(downloadEntity)
} else if (DownloadStatus.unqualified == downloadEntity.status) {
// 未成年
@ -153,40 +159,35 @@ object DownloadObserver {
if (DownloadStatus.done == downloadEntity.status) {
if (mDoneDebouncePair?.first != downloadEntity.url) {
mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis())
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
performDownloadCompleteAction(downloadEntity, downloadManager)
} else {
if (mDoneDebouncePair?.second == 0L
|| System.currentTimeMillis() - (mDoneDebouncePair?.second ?: 0) > 500
) {
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
performDownloadCompleteAction(downloadEntity, downloadManager)
}
}
}
if (downloadEntity.status == DownloadStatus.done) {
mRetryableHashMap.remove(downloadEntity.url)
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", downloadEntity.packageName, ""))
}
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
// 如果已下载大小发生变化,表示成功恢复下载,则重置重试标记
if (downloadEntity.status == DownloadStatus.downloading &&
downloadEntity.progress.toString() != downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD]
) {
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = ""
downloadManager.updateDownloadEntity(downloadEntity)
if (downloadEntity.status == DownloadStatus.downloading) {
mRetryableHashMap[downloadEntity.url] = true
}
}
}
// 添加观察者
DownloadManager.getInstance().addObserver(dataWatcher)
}
private fun performDownloadCompleteAction(
downloadEntity: DownloadEntity,
gameId: String,
downloadManager: DownloadManager
) {
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
@ -221,10 +222,12 @@ object DownloadObserver {
downloadEntity.pluginDesc
)
)
downloadEntity.isPlugin -> Utils.toast(
mApplication,
downloadEntity.name + " - " + platform + " - 下载完成"
)
else -> {
if (!downloadEntity.isVGame()) {
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
@ -242,15 +245,10 @@ object DownloadObserver {
val gameName = downloadEntity.getMetaExtra(Constants.GAME_NAME)
if (simulatorJson.isEmpty()) return
var simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
val isInstalled = PackageUtils.isInstalledFromAllPackage(
HaloApp.getInstance().application,
simulator.apk?.packageName
)
val isInstalledNewSimulator =
SimulatorGameManager.isNewSimulatorInstalled(HaloApp.getInstance().application)
val isInstalledOldSimulator =
SimulatorGameManager.isOldSimulatorInstalled(HaloApp.getInstance().application)
// if (!isInstalled && !isInstalledNewSimulator) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
val newSimulator = Config.getNewSimulatorEntitySetting()
@ -261,7 +259,6 @@ object DownloadObserver {
currentActivity, simulator,
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null
)
// }
SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type)
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
} else {

View File

@ -23,9 +23,6 @@ import org.json.JSONObject
object DownloadDataHelper {
private const val TAG = "DownloadDataHelper"
private const val DOWNLOAD_SPEED_TIME = "download_speed_time"
private const val DOWNLOAD_SPEED_SIZE = "download_speed_size"
const val DOWNLOAD_RESUME_WAY = "download_resume_way"
const val DOWNLOAD_RESUME_MANUAL = "manual"
const val DOWNLOAD_RESUME_AUTO = "auto"
@ -34,8 +31,6 @@ object DownloadDataHelper {
const val DOWNLOAD_CANCEL_MANUAL = "manual"
const val DOWNLOAD_CANCEL_AUTO = "auto"
const val DOWNLOAD_FIRST_START = "download_first_start"
const val DOWNLOAD_THREAD_SIZE = "download_thread_size" // 线程数量0为传统单线程模式; 1为多线程的单线程; >1为多线程的实际线程数量
private val mDownloadSpeedMap = HashMap<String, MutableList<Long>>()
@ -140,26 +135,28 @@ object DownloadDataHelper {
@JvmStatic
fun uploadDownloadEvent(downloadEntity: DownloadEntity) {
val downloadSimpleEntity = DownloadDataSimpleHelper.getDownloadDataSimpleEntity(downloadEntity.url)
if (downloadEntity.status != DownloadStatus.downloading) {
uploadDownloadStatusEvent(downloadEntity)
}
if (downloadEntity.status == DownloadStatus.downloading) {
val startupTime = downloadEntity.meta[DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY]
val startupTime =
downloadSimpleEntity?.downloadStartUpTime
if (startupTime != null) {
uploadDownloadStartupTimeEvent(downloadEntity, startupTime.toInt())
downloadEntity.meta.remove(DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
uploadDownloadStartupTimeEvent(downloadEntity, startupTime)
DownloadDataSimpleHelper.updateDownloadDataSimpleEntity(downloadEntity.url, downloadStartUpTime = null)
}
}
if (downloadEntity.status == DownloadStatus.downloading || downloadEntity.status == DownloadStatus.done) {
val time = downloadEntity.meta[DOWNLOAD_SPEED_TIME]
val size = downloadEntity.meta[DOWNLOAD_SPEED_SIZE]
val time = downloadSimpleEntity?.downloadSpeedTime
val size = downloadSimpleEntity?.downloadSpeedSize
if (downloadEntity.speed == 0L || time == null || size == null) {
downloadEntity.meta[DOWNLOAD_SPEED_TIME] = System.currentTimeMillis().toString()
downloadEntity.meta[DOWNLOAD_SPEED_SIZE] = downloadEntity.progress.toString()
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
val currentTime = System.currentTimeMillis()
val currentProgress = downloadEntity.progress
DownloadDataSimpleHelper.updateDownloadDataSimpleEntity(downloadEntity.url, downloadSpeedTime = currentTime, downloadSpeedSize = currentProgress)
} else {
val offset = System.currentTimeMillis() - time.toLong()
if (offset > 5000) {
@ -180,15 +177,15 @@ object DownloadDataHelper {
}
}
}
downloadEntity.meta[DOWNLOAD_SPEED_TIME] = System.currentTimeMillis().toString()
downloadEntity.meta[DOWNLOAD_SPEED_SIZE] = downloadEntity.progress.toString()
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
val currentTime = System.currentTimeMillis()
val currentProgress = downloadEntity.progress
DownloadDataSimpleHelper.updateDownloadDataSimpleEntity(downloadEntity.url, downloadSpeedTime = currentTime, downloadSpeedSize = currentProgress)
}
}
}
}
private fun uploadDownloadStartupTimeEvent(downloadEntity: DownloadEntity, startupTime: Int) {
private fun uploadDownloadStartupTimeEvent(downloadEntity: DownloadEntity, startupTime: Long) {
val jsonObject = JSONObject()
try {
@ -280,7 +277,7 @@ object DownloadDataHelper {
}
payloadObject.put("completed_size", downloadEntity.progress / 1024 / 1024)
if (downloadEntity.status == DownloadStatus.resume) {
if (downloadEntity.meta[DOWNLOAD_FIRST_START] == "YES") {
if (DownloadDataSimpleHelper.getDownloadDataSimpleEntity(downloadEntity.url)?.isFirstTimeDownload == true) {
payloadObject.put("is_first_start", true)
} else {
payloadObject.put("is_first_start", false)
@ -288,8 +285,7 @@ object DownloadDataHelper {
}
if (downloadEntity.status == DownloadStatus.resume || downloadEntity.status == DownloadStatus.add) {
downloadEntity.meta[DOWNLOAD_FIRST_START] = "NO"
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
DownloadDataSimpleHelper.updateDownloadSimpleEntityFirstTimeDownload(downloadEntity.url, false)
}
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
@ -342,7 +338,7 @@ object DownloadDataHelper {
/**
* 分片检测下载进度,每隔15秒内记录一次,60秒上传一次
*
* 请见:https://gitlab.ghzs.com/stats/stats-issues/-/issues/188#note_66919
* 请见:https://git.shanqu.cc/stats/stats-issues/-/issues/188#note_66919
*/
fun uploadDownloadHeartbeat(upload: Boolean) {
val allDownloadEntity = DownloadManager.getInstance().allDownloadEntity

View File

@ -0,0 +1,60 @@
package com.gh.download
import android.content.Context
import android.content.SharedPreferences
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.utils.toJsonIgnoredNull
import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.core.utils.SPUtils
/**
* 用来简单保存/获取下载时一些额外信息的辅助类,避免下载时反复修改原始下载实体的糟糕操作
* 使用 SP 来实现,只适用于相对较为低频的调用
*/
object DownloadDataSimpleHelper {
private val mSp: SharedPreferences by lazy {
HaloApp.getInstance().getSharedPreferences("DownloadDataSimpleDao", Context.MODE_PRIVATE)
}
fun getDownloadDataSimpleEntity(url: String): DownloadDataSimpleEntity? {
return mSp.getString(url, "")?.toObject()
}
fun updateDownloadDataSimpleEntity(
url: String,
downloadStartUpTime: Long? = null,
downloadSpeedTime: Long? = null,
downloadSpeedSize: Long? = null,
) {
val entity = getDownloadDataSimpleEntity(url) ?: DownloadDataSimpleEntity()
entity.downloadStartUpTime = downloadStartUpTime
entity.downloadSpeedTime = downloadSpeedTime
entity.downloadSpeedSize = downloadSpeedSize
SPUtils.setString(mSp, url, entity.toJsonIgnoredNull())
}
fun updateDownloadSimpleEntityFirstTimeDownload(url: String, isFirstTimeDownload: Boolean) {
val entity = getDownloadDataSimpleEntity(url) ?: DownloadDataSimpleEntity()
entity.isFirstTimeDownload = isFirstTimeDownload
SPUtils.setString(mSp, url, entity.toJsonIgnoredNull())
}
fun removeDownloadSimpleEntity(url: String) {
SPUtils.remove(mSp, url)
}
}
class DownloadDataSimpleEntity(
var downloadStartUpTime: Long? = null,
var downloadSpeedTime: Long? = null,
var downloadSpeedSize: Long? = null,
var isFirstTimeDownload: Boolean? = true
)

View File

@ -834,6 +834,7 @@ public class DownloadManager implements DownloadStatusListener {
*/
public void cancel(String url, boolean isDeleteFile, boolean automatic, boolean cancelSilently) {
DownloadEntity entry = mDownloadDao.getSnapshot(url);
DownloadDataSimpleHelper.INSTANCE.removeDownloadSimpleEntity(url);
if (entry != null) {
AppExecutor.getIoExecutor().execute(() -> {
NDownloadBridge.INSTANCE.cancel(url);
@ -996,8 +997,11 @@ public class DownloadManager implements DownloadStatusListener {
}
if (status == DownloadStatus.add || status == DownloadStatus.subscribe) {
if (downloadEntity.getMeta().get(DownloadDataHelper.DOWNLOAD_FIRST_START) == null) {
downloadEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_FIRST_START, "YES");
DownloadDataSimpleEntity simpleEntity =
DownloadDataSimpleHelper.INSTANCE.getDownloadDataSimpleEntity(downloadEntity.getUrl());
if (simpleEntity == null || simpleEntity.isFirstTimeDownload() == null) {
DownloadDataSimpleHelper.INSTANCE.updateDownloadSimpleEntityFirstTimeDownload(downloadEntity.getUrl(), true);
}
}
@ -1237,7 +1241,10 @@ public class DownloadManager implements DownloadStatusListener {
/**
* 更新数据库中的下载实体
*
* @deprecated 随意更新数据库可能会出现意想不到的情况,建议不要随意调用
*/
@Deprecated()
public void updateDownloadEntity(DownloadEntity downloadEntity) {
mDownloadDao.update(downloadEntity, false);
}

View File

@ -3,6 +3,7 @@ package com.gh.ndownload
import android.text.TextUtils
import androidx.annotation.WorkerThread
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadDataSimpleHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.utils.NetworkUtils
import com.gh.gamecenter.common.utils.addMetaExtra
@ -142,9 +143,7 @@ object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
override fun onProgressWithoutThrottle(id: String?, progress: Float) {
if (id != null && (mStartUpTimeMap[id] ?: 0) > 0) {
val startupTime: Long = System.currentTimeMillis() - (mStartUpTimeMap[id] ?: 0)
getDownloadEntityFromSnapshot(id)?.let { downloadEntity ->
downloadEntity.meta.put(DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY, startupTime.toString())
}
DownloadDataSimpleHelper.updateDownloadDataSimpleEntity(id, downloadStartUpTime = startupTime)
mStartUpTimeMap[id] = 0
}
}

View File

@ -402,6 +402,10 @@ inline fun <reified T : Any> T.toJson(): String {
return GsonUtils.toJson(this)
}
inline fun <reified T : Any> T.toJsonIgnoredNull(): String {
return GsonUtils.toJson(this)
}
fun String.insert(index: Int, string: String): String {
return this.substring(0, index) + string + this.substring(index, this.length)
}

View File

@ -127,4 +127,14 @@ object SPUtils {
editor.remove(key)
editor.apply()
}
/**
* 使用传入的 SP 来移除 KV
*/
@JvmStatic
fun remove(sharedPreferences: SharedPreferences, key: String) {
val editor = sharedPreferences.edit()
editor.remove(key)
editor.apply()
}
}