feat(infrastructure): add except BusinessException case for crash handle (#651)

This commit is contained in:
Ash 2024-03-18 15:06:27 +08:00 committed by GitHub
parent 441368695c
commit 69d7124a76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 115 additions and 34 deletions

View File

@ -8,8 +8,6 @@ import androidx.work.WorkManager
import com.rometools.rome.feed.synd.SyndFeed
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import me.ash.reader.R
@ -27,6 +25,7 @@ import me.ash.reader.infrastructure.android.NotificationHelper
import me.ash.reader.infrastructure.di.DefaultDispatcher
import me.ash.reader.infrastructure.di.IODispatcher
import me.ash.reader.infrastructure.di.MainDispatcher
import me.ash.reader.infrastructure.exception.FeverAPIException
import me.ash.reader.infrastructure.html.Readability
import me.ash.reader.infrastructure.rss.RssHelper
import me.ash.reader.infrastructure.rss.provider.fever.FeverAPI
@ -90,35 +89,35 @@ class FeverRssService @Inject constructor(
feedLink: String, searchedFeed: SyndFeed, groupId: String,
isNotification: Boolean, isFullContent: Boolean,
) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun addGroup(destFeed: Feed?, newGroupName: String): String {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun renameGroup(group: Group) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun renameFeed(feed: Feed) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun deleteGroup(group: Group, onlyDeleteNoStarred: Boolean?) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun deleteFeed(feed: Feed, onlyDeleteNoStarred: Boolean?) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun moveFeed(originGroupId: String, feed: Feed) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
override suspend fun changeFeedUrl(feed: Feed) {
throw Exception("Unsupported")
throw FeverAPIException("Unsupported")
}
/**

View File

@ -4,7 +4,7 @@ import android.content.Context
import android.content.Intent
import android.os.Looper
import android.util.Log
import me.ash.reader.ui.ext.getCurrentVersion
import me.ash.reader.infrastructure.exception.BusinessException
import me.ash.reader.ui.ext.showToastLong
import java.lang.Thread.UncaughtExceptionHandler
@ -23,10 +23,21 @@ class CrashHandler(private val context: Context) : UncaughtExceptionHandler {
override fun uncaughtException(p0: Thread, p1: Throwable) {
val causeMessage = getCauseMessage(p1)
Log.e("RLog", "uncaughtException: $causeMessage", p1)
context.startActivity(Intent(context, CrashReportActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(CrashReportActivity.ERROR_REPORT_KEY, p1.stackTraceToString())
})
when (p1) {
is BusinessException -> {
Looper.myLooper() ?: Looper.prepare()
context.showToastLong(causeMessage)
Looper.loop()
}
else -> {
context.startActivity(Intent(context, CrashReportActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(CrashReportActivity.ERROR_REPORT_KEY, p1.stackTraceToString())
})
}
}
}
private fun getCauseMessage(e: Throwable?): String? {

View File

@ -0,0 +1,14 @@
package me.ash.reader.infrastructure.exception
open class BusinessException : Exception {
constructor() : super()
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : super(cause)
constructor(
message: String,
cause: Throwable,
enableSuppression: Boolean,
writableStackTrace: Boolean,
) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -0,0 +1,14 @@
package me.ash.reader.infrastructure.exception
class FeverAPIException : BusinessException {
constructor() : super()
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : super(cause)
constructor(
message: String,
cause: Throwable,
enableSuppression: Boolean,
writableStackTrace: Boolean,
) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -0,0 +1,14 @@
package me.ash.reader.infrastructure.exception
class GoogleReaderAPIException : BusinessException {
constructor() : super()
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : super(cause)
constructor(
message: String,
cause: Throwable,
enableSuppression: Boolean,
writableStackTrace: Boolean,
) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -0,0 +1,14 @@
package me.ash.reader.infrastructure.exception
class RemoteCallException : BusinessException {
constructor() : super()
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : super(cause)
constructor(
message: String,
cause: Throwable,
enableSuppression: Boolean,
writableStackTrace: Boolean,
) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -0,0 +1,14 @@
package me.ash.reader.infrastructure.exception
class RetryException : BusinessException {
constructor() : super()
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
constructor(cause: Throwable) : super(cause)
constructor(
message: String,
cause: Throwable,
enableSuppression: Boolean,
writableStackTrace: Boolean,
) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -1,5 +1,6 @@
package me.ash.reader.infrastructure.rss.provider.fever
import me.ash.reader.infrastructure.exception.FeverAPIException
import me.ash.reader.infrastructure.rss.provider.ProviderAPI
import me.ash.reader.ui.ext.encodeBase64
import me.ash.reader.ui.ext.md5
@ -29,8 +30,8 @@ class FeverAPI private constructor(
.executeAsync()
when (response.code) {
401 -> throw Exception("Unauthorized")
!in 200..299 -> throw Exception("Forbidden")
401 -> throw FeverAPIException("Unauthorized")
!in 200..299 -> throw FeverAPIException("Forbidden")
}
return toDTO(response.body.string())
@ -38,14 +39,14 @@ class FeverAPI private constructor(
private fun checkAuth(authMap: Map<String, Any>): Int = checkAuth(authMap["auth"] as Int?)
private fun checkAuth(auth: Int?): Int = auth?.takeIf { it > 0 } ?: throw Exception("Unauthorized")
private fun checkAuth(auth: Int?): Int = auth?.takeIf { it > 0 } ?: throw FeverAPIException("Unauthorized")
@Throws
suspend fun validCredentials(): Int = checkAuth(postRequest<FeverDTO.Common>(null).auth)
suspend fun getApiVersion(): Long =
postRequest<Map<String, Any>>(null)["api_version"] as Long?
?: throw Exception("Unable to get version")
?: throw FeverAPIException("Unable to get version")
suspend fun getGroups(): FeverDTO.Groups =
postRequest<FeverDTO.Groups>("groups").apply { checkAuth(auth) }
@ -66,7 +67,7 @@ class FeverAPI private constructor(
postRequest<FeverDTO.Items>("items&max_id=$id").apply { checkAuth(auth) }
suspend fun getItemsWith(ids: List<String>): FeverDTO.Items =
if (ids.size > 50) throw Exception("Too many ids")
if (ids.size > 50) throw FeverAPIException("Too many ids")
else postRequest<FeverDTO.Items>("items&with_ids=${ids.joinToString(",")}").apply { checkAuth(auth) }
suspend fun getLinks(): FeverDTO.Links =

View File

@ -1,6 +1,8 @@
package me.ash.reader.infrastructure.rss.provider.greader
import me.ash.reader.infrastructure.di.USER_AGENT_STRING
import me.ash.reader.infrastructure.exception.GoogleReaderAPIException
import me.ash.reader.infrastructure.exception.RetryException
import me.ash.reader.infrastructure.rss.provider.ProviderAPI
import okhttp3.FormBody
import okhttp3.Request
@ -32,11 +34,11 @@ class GoogleReaderAPI private constructor(
private val authData = AuthData(null, null)
suspend fun validCredentials(): Boolean {
reauthenticate()
reAuthenticate()
return authData.clientLoginToken?.isNotEmpty() ?: false
}
private suspend fun reauthenticate() {
private suspend fun reAuthenticate() {
// Get client login token
val clResponse = client.newCall(
Request.Builder()
@ -55,10 +57,10 @@ class GoogleReaderAPI private constructor(
val clBody = clResponse.body.string()
when (clResponse.code) {
400 -> throw Exception("BadRequest for CL Token")
401 -> throw Exception("Unauthorized for CL Token")
400 -> throw GoogleReaderAPIException("BadRequest for CL Token")
401 -> throw GoogleReaderAPIException("Unauthorized for CL Token")
!in 200..299 -> {
throw Exception(clBody)
throw GoogleReaderAPIException(clBody)
}
}
@ -69,7 +71,7 @@ class GoogleReaderAPI private constructor(
.split("\n")
.find { it.startsWith("Auth=") }
?.substring(5)
?: throw Exception("body format error for CL Token:\n$clBody")
?: throw GoogleReaderAPIException("body format error for CL Token:\n$clBody")
}
// Get action token
@ -88,8 +90,6 @@ class GoogleReaderAPI private constructor(
authData.actionToken = actBody
}
class RetryException(message: String) : Exception(message)
private suspend inline fun <reified T> retryableGetRequest(
query: String,
params: List<Pair<String, String>>? = null,
@ -122,7 +122,7 @@ class GoogleReaderAPI private constructor(
params: List<Pair<String, String>>? = null,
): T {
if (authData.clientLoginToken.isNullOrEmpty()) {
reauthenticate()
reAuthenticate()
}
val response = client.newCall(
@ -136,7 +136,7 @@ class GoogleReaderAPI private constructor(
val body = response.body.string()
when (response.code) {
400 -> throw Exception("BadRequest")
400 -> throw GoogleReaderAPIException("BadRequest")
401 -> throw RetryException("Unauthorized")
!in 200..299 -> {
val gReaderError = try {
@ -144,7 +144,7 @@ class GoogleReaderAPI private constructor(
} catch (ignore: Exception) {
GoogleReaderDTO.GReaderError(listOf(body))
}
throw Exception(gReaderError.errors.joinToString(";\n "))
throw GoogleReaderAPIException(gReaderError.errors.joinToString(";\n "))
}
}
@ -157,7 +157,7 @@ class GoogleReaderAPI private constructor(
form: List<Pair<String, String>>? = null,
): T {
if (authData.clientLoginToken.isNullOrEmpty()) {
reauthenticate()
reAuthenticate()
}
val response = client.newCall(
Request.Builder()
@ -175,10 +175,10 @@ class GoogleReaderAPI private constructor(
val responseBody = response.body.string()
when (response.code) {
400 -> throw Exception("BadRequest")
400 -> throw GoogleReaderAPIException("BadRequest")
401 -> throw RetryException("Unauthorized")
!in 200..299 -> {
throw Exception(responseBody)
throw GoogleReaderAPIException(responseBody)
}
}