Fix: prevent black screen after failed initialization.

If a crash occurs before feedback is started then attempting to report that crash will, itself, crash because the lateinit feedback instance is not initialized. The result is a black screen on launch! This fixes that by catching everything while trying to report an error.
This commit is contained in:
Kevin Gorham 2020-08-27 20:24:01 -04:00
parent 953aeb32ea
commit 1577b3223d
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
2 changed files with 38 additions and 14 deletions

View File

@ -7,10 +7,8 @@ import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraXConfig
import cash.z.ecc.android.di.component.AppComponent
import cash.z.ecc.android.di.component.DaggerAppComponent
import cash.z.ecc.android.ext.tryWithWarning
import cash.z.ecc.android.feedback.FeedbackCoordinator
import cash.z.ecc.android.sdk.ext.SilentTwig
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.*
import javax.inject.Inject
@ -68,19 +66,34 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
* is complete, we can lazily initialize all the feedback objects at this moment so that we
* don't have to add any time to startup.
*/
inner class ExceptionReporter(private val ogHandler: Thread.UncaughtExceptionHandler) : Thread.UncaughtExceptionHandler {
inner class ExceptionReporter(private val ogHandler: Thread.UncaughtExceptionHandler) :
Thread.UncaughtExceptionHandler {
override fun uncaughtException(t: Thread?, e: Throwable?) {
twig("Uncaught Exception: $e caused by: ${e?.cause}")
// these are the only reported crashes that are considered fatal
coordinator.feedback.report(e, true)
coordinator.flush()
// can do this if necessary but first verify that we need it
runBlocking {
coordinator.await()
coordinator.feedback.stop()
// Things can get pretty crazy during a fatal exception
// so be cautious here to avoid freezing the app
tryWithWarning("Unable to report fatal crash") {
// note: these are the only reported crashes that set isFatal=true
coordinator.feedback.report(e, true)
}
tryWithWarning("Unable to flush the feedback coordinator") {
coordinator.flush()
}
try {
// can do this if necessary but first verify that we need it
runBlocking {
coordinator.await()
coordinator.feedback.stop()
}
} catch (t: Throwable) {
twig("WARNING: failed to wait for the feedback observers to complete.")
} finally {
// it's important that this always runs so we use the finally clause here
// rather than another tryWithWarning block
ogHandler.uncaughtException(t, e)
Thread.sleep(2000L)
}
ogHandler.uncaughtException(t, e)
Thread.sleep(2000L)
}
}
}

View File

@ -1,3 +1,14 @@
package cash.z.ecc.android.ext
fun Boolean.asString(ifTrue: String = "", ifFalse: String = "") = if(this) ifTrue else ifFalse
import cash.z.ecc.android.sdk.ext.twig
fun Boolean.asString(ifTrue: String = "", ifFalse: String = "") = if(this) ifTrue else ifFalse
inline fun <R> tryWithWarning(message: String = "", block: () -> R): R? {
return try {
block()
} catch (error: Throwable) {
twig("WARNING: $message")
null
}
}