Upgrade to Pro — share decks privately, control downloads, hide ads and more …

android app error handling

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

android app error handling

Avatar for Shinnosuke Kugimiya

Shinnosuke Kugimiya

January 26, 2021
Tweet

More Decks by Shinnosuke Kugimiya

Other Decks in Programming

Transcript

  1. ·ͣ͸ચ͍ग़ͦ͏ Πϯϑϥ૚ ϓϨθϯςʔγϣϯ૚ "DUJWJUZ'SBHNFOU 7JFX.PEFM 3FQPTJUPSZ "1*$MJFOU %# Ϣʔεέʔε૚ 6TF$BTF

    υϝΠϯ૚ ϞσϦϯάͨ͠Ϋϥε܈ ೝূܥͷΤϥʔ αʔϏεܥͷΤϥʔ %#ʹؔ͢ΔΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ "OESPJEʹؔ͢ΔΤϥʔ ௨৴ෆೳܥͷΤϥʔ
  2. Πϯϑϥ૚ ϓϨθϯςʔγϣϯ૚ "DUJWJUZ'SBHNFOU 7JFX.PEFM 3FQPTJUPSZ "1*$MJFOU %# Ϣʔεέʔε૚ 6TF$BTF υϝΠϯ૚

    ϞσϦϯάͨ͠Ϋϥε܈ ೝূܥͷΤϥʔ αʔϏεܥͷΤϥʔ %#ʹؔ͢ΔΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ "OESPJEʹؔ͢ΔΤϥʔ ௨৴ෆೳܥͷΤϥʔ ΧςΰϥΠζ͠Α͏ w αʔϏεʹؔΘΔΤϥʔ w ೝূΤϥʔ w αʔϏεಠࣗͷΤϥʔ w αʔϏεʹΑΒͳ͍Τϥʔ w ௨৴Τϥʔ w %#઀ଓෆྑΤϥʔ w "OESPJEʹؔ͢ΔΤϥʔ
  3. Τϥʔͷॲཧͷ࢓ํΛߟ͑Δ w ݸผॲཧʢͦͷը໘Ͱ͔͠ൃੜ͠ͳ͍Τϥʔͷॲཧͷ࢓ํʣ w ڞ௨ॲཧʢͲͷը໘Ͱ΋ى͖ΔΤϥʔͷॲཧͷ࢓ํʣ w μΠΞϩά w εφοΫόʔ w

    ڧ੍ը໘ભҠʢϩάΠϯը໘΍ϝϯςφϯεը໘ʹඈ͹͢ͳͲʣ w $SBTIMZUJDTʹUISPXBCMFΛͱΓ͋͑ͣ౤͓͛ͯ͘ w ॲཧ͠ͳ͍ʢͦͷ··$SBTIͤ͞Δʣ
  4. ϓϨθϯςʔγϣϯ૚ "DUJWJUZ'SBHNFOU 7JFX.PEFM Ϣʔεέʔε૚ 6TF$BTF υϝΠϯ૚ ϞσϦϯάͨ͠Ϋϥε܈ ঢ়ଶҟৗͳͲͷΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ "OESPJEʹؔ͢ΔΤϥʔ

    $BUDI͢Δ͠ͳ͍ͷڥքઢΛܾΊΔ ঢ়ଶҟৗͳͲͷΤϥʔ DBUDI͢Δ DBUDI͠ͳ͍ ࢲͷ৔߹͸ "OESPJEʹؔ͢ΔΤϥʔ͸ DBUDI͠ͳ͍ɻ
  5. ϢʔβʔϏϦςΟ΋ߟ͑Δ w ͜ͷ࣌ʹ௨৴ෆྑͰશͯͷ௨ ৴͕ࣦഊͨ͠ͱ͢Δ w ࣍ͷμΠΞϩά͕Ξλονͯ͠ ͍ΔμΠΞϩάͷ਺͚ͩͰΔ ͷ͸๷͍͗ͨ "Ϟδϡʔϧͷ"'SBHNFOU #Ϟδϡʔϧͷ#'SBHNFOU

    $Ϟδϡʔϧͷ$'SBHNFOU ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ
  6. ΤϥʔΛΩϟονͯ͠-JWF%BUBʹ٧ΊΔ class UserViewModel : ViewModel() { … private val _error

    = MutableLiveData<Throwable>() val error: LiveData<Throwable> = _error fun onCreateView() = viewModelScope.launch { try { _user.value = useCase.execute() } catch (t: Throwable) { _error.value = t } } }
  7. ΤϥʔΛॲཧ͢Δ class UserFragment : Fragment() { … override fun onViewCreated(view:

    View, savedInstanceState: Bundle?) { … viewModel.error.observe(viewLifecycleOwner) { if (it is NotFoundUserException) { showNotFound() return@observe } errorHandler.handle( requireContext(), view.rootView, it ) } } private fun showNotFound() { … } }
  8. ΤϥʔΛॲཧ͢Δʢݸผͷ৔߹͸ݸผʹʣ class UserFragment : Fragment() { … override fun onViewCreated(view:

    View, savedInstanceState: Bundle?) { … viewModel.error.observe(viewLifecycleOwner) { if (it is NotFoundUserException) { showNotFound() return@observe } errorHandler.handle( requireContext(), view.rootView, it ) } } private fun showNotFound() { … } }
  9. ΤϥʔΛॲཧ͢Δʢڞ௨ͷ৔߹͸Τϥʔϋϯυϥʔʹʣ class UserFragment : Fragment() { … override fun onViewCreated(view:

    View, savedInstanceState: Bundle?) { … viewModel.error.observe(viewLifecycleOwner) { if (it is NotFoundUserException) { showNotFound() return@observe } errorHandler.handle( requireContext(), view.rootView, it ) } } private fun showNotFound() { … } }
  10. &SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList<ErrorRunnable>() private

    val handler = Handler(Looper.getMainLooper()) fun handle( context: Context, view: View, throwable: Throwable ) { val runnable = ErrorRunnable( Error.convert(context, view, throwable) ) if (queue.none { it.error == runnable.error }) { queue.add(runnable) handler.post(runnable) } } inner class ErrorRunnable(val error: Error) : Runnable { override fun run() { error.handle() val running = this handler.postDelayed({ queue.remove(running) }, 200) } } }
  11. &SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList<ErrorRunnable>() private

    val handler = Handler(Looper.getMainLooper()) fun handle( context: Context, view: View, throwable: Throwable ) { val runnable = ErrorRunnable( Error.convert(context, view, throwable) ) if (queue.none { it.error == runnable.error }) { queue.add(runnable) handler.post(runnable) } } inner class ErrorRunnable(val error: Error) : Runnable { override fun run() { error.handle() val running = this handler.postDelayed({ queue.remove(running) }, 200) } } } 5ISPXBCMF&SSPS
  12. &SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList<ErrorRunnable>() private

    val handler = Handler(Looper.getMainLooper()) fun handle( context: Context, view: View, throwable: Throwable ) { val runnable = ErrorRunnable( Error.convert(context, view, throwable) ) if (queue.none { it.error == runnable.error }) { queue.add(runnable) handler.post(runnable) } } inner class ErrorRunnable(val error: Error) : Runnable { override fun run() { error.handle() val running = this handler.postDelayed({ queue.remove(running) }, 200) } } }
  13. &SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList<ErrorRunnable>() private

    val handler = Handler(Looper.getMainLooper()) fun handle( context: Context, view: View, throwable: Throwable ) { val runnable = ErrorRunnable( Error.convert(context, view, throwable) ) if (queue.none { it.error == runnable.error }) { queue.add(runnable) handler.post(runnable) } } inner class ErrorRunnable(val error: Error) : Runnable { override fun run() { error.handle() val running = this handler.postDelayed({ queue.remove(running) }, 200) } } }
  14. &SSPS sealed class Error { abstract fun handle() class MaintenanceError(

    private val context: Context ) : Error() { override fun handle() { context.startActivity( MaintenanceActivity.createIntent(context).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) } ) } override fun equals(other: Any?): Boolean = other is MaintenanceError override fun hashCode(): Int { return context.hashCode() } } class UnknownError( private val view: View, private val throwable: Throwable ) : Error() { override fun handle() { FirebaseCrashlytics.getInstance().recordException(throwable) Snackbar.make(view, "ෆ໌ͳΤϥʔͰ͢", Snackbar.LENGTH_SHORT).show() } } companion object { fun convert( context: Context, view: View, throwable: Throwable ): Error = when { throwable is HttpException && throwable.code() == 503 -> MaintenanceError(context) else -> UnknownError(view, throwable) } } }
  15. &SSPS sealed class Error { abstract fun handle() class MaintenanceError(

    private val context: Context ) : Error() { override fun handle() { context.startActivity( MaintenanceActivity.createIntent(context).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) } ) } override fun equals(other: Any?): Boolean = other is MaintenanceError override fun hashCode(): Int { return context.hashCode() } } class UnknownError(…) {…} … }
  16. &SSPS sealed class Error { abstract fun handle() class MaintenanceError(

    private val context: Context ) : Error() { override fun handle() { context.startActivity( MaintenanceActivity.createIntent(context).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) } ) } override fun equals(other: Any?): Boolean = other is MaintenanceError override fun hashCode(): Int { return context.hashCode() } } class UnknownError(…) {…} … }
  17. &SSPS sealed class Error { … companion object { fun

    convert( context: Context, view: View, throwable: Throwable ): Error = when { throwable is HttpException && throwable.code() == 503 -> MaintenanceError(context) … else -> UnknownError(view, throwable) } } }