Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guoxiangrui/feature/remove databinding #514

Merged
merged 9 commits into from
Jan 4, 2025
Prev Previous commit
Next Next commit
🐛 pro 打包下 CrashMonitor 被其他 sdk 覆盖导致未生效,配置 OkHttp 默认线程分发器以拦截异常
985892345 committed Dec 29, 2024
commit cdef6cd51d771a97d823444166995d9c25d1a914
2 changes: 1 addition & 1 deletion cyxbs-components/base/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ plugins {
id("kmp.compose")
}

useKtProvider(false) // base 模块不包含实现类,不需要处理注解
useKtProvider()
useDataBinding(false) // base 模块只依赖 DataBinding 但不开启 DataBinding

kotlin {
Original file line number Diff line number Diff line change
@@ -6,37 +6,41 @@ import android.util.Log
import com.cyxbs.components.base.BuildConfig
import com.cyxbs.components.base.crash.CrashActivity.Companion.NetworkApiResult
import com.cyxbs.components.base.pages.SecretActivity
import com.g985892345.provider.api.annotation.ImplProvider
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import java.lang.Thread.UncaughtExceptionHandler

/**
* .
*
* @author 985892345
* @date 2024/12/25
*/
object CrashMonitor {
@ImplProvider // 提供给其他模块使用,比如 ApiGenerator 中的 OkHttp Dispatcher
object CrashMonitor : UncaughtExceptionHandler {

// 提供给 application 模块向外暴露异常用于上报
var crashReport: ((Throwable) -> Unit)? = null

private var lastThrowableTime = 0L

private val mainThread = Looper.getMainLooper().thread

fun install() {
installThreadHandler()
installRxjavaErrorHandler()
}

private fun installThreadHandler() {
Thread.setDefaultUncaughtExceptionHandler { t, e ->
if (t === Looper.getMainLooper().thread) {
if (BuildConfig.DEBUG) {
Log.d("crash", e.stackTraceToString())
}
handleMainThread(t, e)
crashReport?.invoke(e)
} else {
handleOtherThread(t, e)
// 这里的 ExceptionHandler 优先级会高于 DefaultUncaughtExceptionHandler
mainThread.setUncaughtExceptionHandler(this)
Thread.setDefaultUncaughtExceptionHandler(this)
Looper.getMainLooper().queue.addIdleHandler {
// 我们需要确保 DefaultUncaughtExceptionHandler 没有被其他 sdk 覆盖掉
if (Thread.getDefaultUncaughtExceptionHandler() !== this) {
Thread.setDefaultUncaughtExceptionHandler(this)
}
true
}
}

@@ -46,11 +50,14 @@ object CrashMonitor {
}
}

private fun handleOtherThread(thread: Thread, throwable: Throwable) {
private fun handleOtherThread(throwable: Throwable) {
// 其他线程不处理
if (BuildConfig.DEBUG) {
Log.d("OtherThread", throwable.stackTraceToString())
}
}

private fun handleMainThread(thread: Thread, throwable: Throwable) {
private fun handleMainThread(throwable: Throwable) {
CrashDialog.Builder(
RuntimeException(
"触发了一次来自主线程的异常, ${throwable.message}",
@@ -65,7 +72,7 @@ object CrashMonitor {
// 主线程崩溃后 loop 会停掉,这里重启 loop
try {
Looper.loop()
} catch (e: Exception) {
} catch (e: Throwable) {
e.printStackTrace()
if (SystemClock.elapsedRealtime() - lastThrowableTime < 1000) {
// 短时间内再次崩溃,则直接打开 CrashActivity
@@ -86,4 +93,16 @@ object CrashMonitor {
}
}
}

override fun uncaughtException(t: Thread, e: Throwable) {
if (t === mainThread) {
if (BuildConfig.DEBUG) {
Log.d("crash", e.stackTraceToString())
}
handleMainThread(e)
crashReport?.invoke(e)
} else {
handleOtherThread(e)
}
}
}
Original file line number Diff line number Diff line change
@@ -10,11 +10,13 @@ import com.cyxbs.components.utils.extensions.appContext
import com.cyxbs.components.utils.extensions.defaultJson
import com.cyxbs.components.utils.service.allImpl
import com.cyxbs.components.utils.service.impl
import com.cyxbs.components.utils.service.implOrNull
import com.cyxbs.components.utils.utils.LogLocal
import com.cyxbs.components.utils.utils.LogUtils
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import okhttp3.Dispatcher
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
@@ -26,6 +28,9 @@ import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
@@ -86,6 +91,22 @@ object ApiGenerator {

private val mAccountService = IAccountService::class.impl()

// 手动创建 okhttp 的线程分发器,规避 协程 + Retrofit 在子线程请求被 cancel 后的异常问题
private val OkHttpDispatcher = Dispatcher(
ThreadPoolExecutor(
0, 64, 60, TimeUnit.SECONDS,
SynchronousQueue(),
ThreadFactory {
Thread(it, "ApiGenerator OkHttp Dispatcher").apply {
// 这里设置线程的异常处理器,默认给 base 模块的 CrashMonitor
setUncaughtExceptionHandler(
Thread.UncaughtExceptionHandler::class.implOrNull()
)
}
}
)
)

//init对两种公共的retrofit进行配置
init {
val networkConfigs = INetworkConfigService::class.allImpl()
@@ -237,6 +258,7 @@ object ApiGenerator {
private fun OkHttpClient.Builder.defaultConfig() {
this.connectTimeout(DEFAULT_TIME_OUT.toLong(), TimeUnit.SECONDS)
this.readTimeout(DEFAULT_TIME_OUT.toLong(), TimeUnit.SECONDS)
dispatcher(OkHttpDispatcher)
}