mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Log app scope failures instead of crashing outside debug builds
This commit is contained in:
@@ -4,15 +4,20 @@ import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.BindingContainer
|
||||
import dev.zacsweers.metro.Provides
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import mihon.core.metro.IsDebugBuild
|
||||
import mihon.telemetry.TelemetryConfig
|
||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
||||
@BindingContainer
|
||||
object AppBindings {
|
||||
@@ -20,7 +25,20 @@ object AppBindings {
|
||||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
@AppCoroutineScope
|
||||
fun providesAppCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
fun providesAppCoroutineScope(@IsDebugBuild isDebugBuild: Boolean): CoroutineScope {
|
||||
// Work here outlives the screen that started it, so a failure would crash the app on whatever screen is open
|
||||
// by then. Debug builds still crash so the bug gets noticed.
|
||||
val handler = CoroutineExceptionHandler { _, throwable ->
|
||||
if (isDebugBuild) {
|
||||
val thread = Thread.currentThread()
|
||||
thread.uncaughtExceptionHandler?.uncaughtException(thread, throwable)
|
||||
} else {
|
||||
logcat(LogPriority.ERROR, throwable) { "Uncaught exception in the app scope" }
|
||||
TelemetryConfig.recordException(throwable)
|
||||
}
|
||||
}
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.IO + handler)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
|
||||
@@ -9,6 +9,9 @@ import dev.zacsweers.metro.Qualifier
|
||||
* [kotlinx.coroutines.Dispatchers.IO], so a failure in one child never takes the others down. Use it for work that is
|
||||
* owned by an application scoped component and must outlive any screen. Work that belongs to a screen, a view model or
|
||||
* another cancellable lifecycle keeps its own scope.
|
||||
*
|
||||
* A child that fails with an uncaught exception is logged and reported instead of crashing the app, except in debug
|
||||
* builds, since by the time it fails the screen that started it may be long gone.
|
||||
*/
|
||||
@Qualifier
|
||||
@Target(
|
||||
|
||||
@@ -59,6 +59,10 @@ object TelemetryConfig {
|
||||
.getCertificateFingerprints()
|
||||
.any { it == MIHON_CERTIFICATE_FINGERPRINT }
|
||||
}
|
||||
|
||||
fun recordException(throwable: Throwable) {
|
||||
crashlytics?.recordException(throwable)
|
||||
}
|
||||
}
|
||||
|
||||
private val MIHON_PACKAGES = hashSetOf("app.mihon", "app.mihon.debug")
|
||||
|
||||
@@ -9,4 +9,6 @@ object TelemetryConfig {
|
||||
fun setAnalyticsEnabled(enabled: Boolean) = Unit
|
||||
|
||||
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
|
||||
|
||||
fun recordException(throwable: Throwable) = Unit
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user