fix(sdk-update): 修复 checkAppUpdate NPE — 服务端 versionName 为 null 时崩溃
Gson 反序列化时会将 JSON null 注入 Kotlin non-null String 字段,绕过默认值。 引入内部 DTO UpdateInfoDto(所有字符串字段可空),转换为 UpdateInfo 时用 orEmpty() 补默认值。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
7b52d09758
当前提交
ba8fc74740
@ -184,7 +184,7 @@ object UpdateSDK {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
api.checkUpdate(XuqmSDK.appKey, "ANDROID", versionCode, userId).data?.let { info ->
|
api.checkUpdate(XuqmSDK.appKey, "ANDROID", versionCode, userId).data?.toUpdateInfo()?.let { info ->
|
||||||
val normalized = info.copy(downloadUrl = normalizeDownloadUrl(info.downloadUrl) ?: info.downloadUrl)
|
val normalized = info.copy(downloadUrl = normalizeDownloadUrl(info.downloadUrl) ?: info.downloadUrl)
|
||||||
val afterIgnore = if (!bypassIgnore && normalized.needsUpdate && !normalized.forceUpdate
|
val afterIgnore = if (!bypassIgnore && normalized.needsUpdate && !normalized.forceUpdate
|
||||||
&& isVersionIgnored(context, normalized.versionCode)
|
&& isVersionIgnored(context, normalized.versionCode)
|
||||||
|
|||||||
@ -6,12 +6,39 @@ import retrofit2.http.Query
|
|||||||
|
|
||||||
data class ApiResponse<T>(val code: Int, val data: T?, val message: String)
|
data class ApiResponse<T>(val code: Int, val data: T?, val message: String)
|
||||||
|
|
||||||
interface UpdateApi {
|
/** 服务端响应的原始 DTO,所有字符串字段可为 null,防止 Gson 绕过 Kotlin 默认值引发 NPE。 */
|
||||||
|
internal data class UpdateInfoDto(
|
||||||
|
val needsUpdate: Boolean = false,
|
||||||
|
val versionName: String? = null,
|
||||||
|
val versionCode: Int = 0,
|
||||||
|
val downloadUrl: String? = null,
|
||||||
|
val changeLog: String? = null,
|
||||||
|
val forceUpdate: Boolean = false,
|
||||||
|
val appStoreUrl: String? = null,
|
||||||
|
val marketUrl: String? = null,
|
||||||
|
val requiresLogin: Boolean = false,
|
||||||
|
val apkHash: String? = null,
|
||||||
|
) {
|
||||||
|
fun toUpdateInfo() = UpdateInfo(
|
||||||
|
needsUpdate = needsUpdate,
|
||||||
|
versionName = versionName.orEmpty(),
|
||||||
|
versionCode = versionCode,
|
||||||
|
downloadUrl = downloadUrl.orEmpty(),
|
||||||
|
changeLog = changeLog.orEmpty(),
|
||||||
|
forceUpdate = forceUpdate,
|
||||||
|
appStoreUrl = appStoreUrl.orEmpty(),
|
||||||
|
marketUrl = marketUrl.orEmpty(),
|
||||||
|
requiresLogin = requiresLogin,
|
||||||
|
apkHash = apkHash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface UpdateApi {
|
||||||
@GET("api/v1/updates/app/check")
|
@GET("api/v1/updates/app/check")
|
||||||
suspend fun checkUpdate(
|
suspend fun checkUpdate(
|
||||||
@Query("appKey") appKey: String,
|
@Query("appKey") appKey: String,
|
||||||
@Query("platform") platform: String,
|
@Query("platform") platform: String,
|
||||||
@Query("currentVersionCode") currentVersionCode: Int,
|
@Query("currentVersionCode") currentVersionCode: Int,
|
||||||
@Query("userId") userId: String? = null,
|
@Query("userId") userId: String? = null,
|
||||||
): ApiResponse<UpdateInfo>
|
): ApiResponse<UpdateInfoDto>
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户