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.BindingContainer
|
||||||
import dev.zacsweers.metro.Provides
|
import dev.zacsweers.metro.Provides
|
||||||
import dev.zacsweers.metro.SingleIn
|
import dev.zacsweers.metro.SingleIn
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
|
import logcat.LogPriority
|
||||||
import mihon.core.metro.AppCoroutineScope
|
import mihon.core.metro.AppCoroutineScope
|
||||||
|
import mihon.core.metro.IsDebugBuild
|
||||||
|
import mihon.telemetry.TelemetryConfig
|
||||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||||
import nl.adaptivity.xmlutil.serialization.XML
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
@BindingContainer
|
@BindingContainer
|
||||||
object AppBindings {
|
object AppBindings {
|
||||||
@@ -20,7 +25,20 @@ object AppBindings {
|
|||||||
@Provides
|
@Provides
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
@AppCoroutineScope
|
@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
|
@Provides
|
||||||
@SingleIn(AppScope::class)
|
@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
|
* [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
|
* 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.
|
* 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
|
@Qualifier
|
||||||
@Target(
|
@Target(
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ object TelemetryConfig {
|
|||||||
.getCertificateFingerprints()
|
.getCertificateFingerprints()
|
||||||
.any { it == MIHON_CERTIFICATE_FINGERPRINT }
|
.any { it == MIHON_CERTIFICATE_FINGERPRINT }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun recordException(throwable: Throwable) {
|
||||||
|
crashlytics?.recordException(throwable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val MIHON_PACKAGES = hashSetOf("app.mihon", "app.mihon.debug")
|
private val MIHON_PACKAGES = hashSetOf("app.mihon", "app.mihon.debug")
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ object TelemetryConfig {
|
|||||||
fun setAnalyticsEnabled(enabled: Boolean) = Unit
|
fun setAnalyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
|
||||||
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
|
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
|
||||||
|
fun recordException(throwable: Throwable) = Unit
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user