Slide 1

Slide 1 text

!LHNZTIJO ΤϥʔϋϯυϦϯά

Slide 2

Slide 2 text

ࣗݾ঺հ

Slide 3

Slide 3 text

ࣗݾ঺հ w ఝٶͰ͢ w ߹ಉձࣾ%..DPN$50ࣨॴଐ

Slide 4

Slide 4 text

͜͏͍͏͜ͱ͋Γ·ͤΜ͔ʁ

Slide 5

Slide 5 text

w ѲΓ௵͢ͳͬͯݴΘΕͯΔ͚Ͳɺ͡Ό͊Ͳ͏͢Ε͹͍͍ͷʁͬͯࢥͬͯΔਓɺ ࣮͸݁ߏ͍ΔΜ͡Όͳ͍ΜͰ͔͢ʁ

Slide 6

Slide 6 text

w ѲΓ௵͢ͳͬͯݴΘΕͯΔ͚Ͳɺ͡Ό͊Ͳ͏͢Ε͹͍͍ͷʁͬͯࢥͬͯΔਓɺ ࣮͸݁ߏ͍ΔΜ͡Όͳ͍ΜͰ͔͢ʁ w ద౰ʹ΍Δͱɺ$SBTI'SFF཰͕ͱΜͰ΋ͳ͍͜ͱʹʜ

Slide 7

Slide 7 text

ߟ͑Δ΂͖͜ͱ

Slide 8

Slide 8 text

ߟ͑Δ΂͖͜ͱ w ·ͣ͸ચ͍ग़ͦ͏ w ΧςΰϥΠζ͠·͠ΐ͏ w ॲཧͷ࢓ํΛચ͍ग़͠·͠ΐ͏ w ΧςΰϥΠζͨ͠ΤϥʔͱॲཧΛϚοϐϯά͠·͠ΐ͏ w ϢʔβϏϦςΟ΋ߟ͑Α͏

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Τϥʔͷॲཧͷ࢓ํΛߟ͑Δ w ݸผॲཧʢͦͷը໘Ͱ͔͠ൃੜ͠ͳ͍Τϥʔͷॲཧͷ࢓ํʣ w ڞ௨ॲཧʢͲͷը໘Ͱ΋ى͖ΔΤϥʔͷॲཧͷ࢓ํʣ w μΠΞϩά w εφοΫόʔ w ڧ੍ը໘ભҠʢϩάΠϯը໘΍ϝϯςφϯεը໘ʹඈ͹͢ͳͲʣ w $SBTIMZUJDTʹUISPXBCMFΛͱΓ͋͑ͣ౤͓͛ͯ͘ w ॲཧ͠ͳ͍ʢͦͷ··$SBTIͤ͞Δʣ

Slide 13

Slide 13 text

ϓϨθϯςʔγϣϯ૚ "DUJWJUZ'SBHNFOU 7JFX.PEFM Ϣʔεέʔε૚ 6TF$BTF υϝΠϯ૚ ϞσϦϯάͨ͠Ϋϥε܈ ঢ়ଶҟৗͳͲͷΤϥʔ ঢ়ଶҟৗͳͲͷΤϥʔ "OESPJEʹؔ͢ΔΤϥʔ $BUDI͢Δ͠ͳ͍ͷڥքઢΛܾΊΔ ঢ়ଶҟৗͳͲͷΤϥʔ DBUDI͢Δ DBUDI͠ͳ͍ ࢲͷ৔߹͸ "OESPJEʹؔ͢ΔΤϥʔ͸ DBUDI͠ͳ͍ɻ

Slide 14

Slide 14 text

ΧςΰϥΠζͨ͠ΤϥʔͱॲཧΛϚοϐϯά͢Δ ೝূΤϥʔ ະ஌ͷΤϥʔ ঢ়ଶҟৗܥΤϥʔ ௨৴Τϥʔ %#઀ଓෆྑΤϥʔ ϩάΠϯը໘ʹભҠ ϝϯςφϯεը໘ʹભҠ ϝϯςφϯε μΠΞϩάදࣔ 4OBDLCBSදࣔ $SBTIMJUZDTʹ౤͛Δ

Slide 15

Slide 15 text

ϢʔβʔϏϦςΟ΋ߟ͑Δ w ྫ͑͹ϚϧνϞδϡʔϧΛ࠾ ༻͍ͯ͠ΔϓϩδΣΫτͰɺ .BJOʹͳΔΑ͏ͳը໘͕࣍ͷ Α͏ʹෳ਺ͷ'SBHNFOUΛΞλ ον͍ͯ͠Δ࣌ "Ϟδϡʔϧͷ"'SBHNFOU #Ϟδϡʔϧͷ#'SBHNFOU $Ϟδϡʔϧͷ$'SBHNFOU

Slide 16

Slide 16 text

ϢʔβʔϏϦςΟ΋ߟ͑Δ w ͜ͷ࣌ʹ௨৴ෆྑͰશͯͷ௨ ৴͕ࣦഊͨ͠ͱ͢Δ w ࣍ͷμΠΞϩά͕Ξλονͯ͠ ͍ΔμΠΞϩάͷ਺͚ͩͰΔ ͷ͸๷͍͗ͨ "Ϟδϡʔϧͷ"'SBHNFOU #Ϟδϡʔϧͷ#'SBHNFOU $Ϟδϡʔϧͷ$'SBHNFOU ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ ௨৴Τϥʔ͕ൃੜ͠·ͨ͠ɻ ࣌ؒΛ͓͍͓ͯࢼ͍ͩ͘͠͞ɻ

Slide 17

Slide 17 text

ίʔυྫ

Slide 18

Slide 18 text

ΤϥʔΛΩϟονͯ͠-JWF%BUBʹ٧ΊΔ class UserViewModel : ViewModel() { … private val _error = MutableLiveData() val error: LiveData = _error fun onCreateView() = viewModelScope.launch { try { _user.value = useCase.execute() } catch (t: Throwable) { _error.value = t } } }

Slide 19

Slide 19 text

ΤϥʔΛॲཧ͢Δ 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() { … } }

Slide 20

Slide 20 text

ΤϥʔΛॲཧ͢Δʢݸผͷ৔߹͸ݸผʹʣ 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() { … } }

Slide 21

Slide 21 text

ΤϥʔΛॲཧ͢Δʢڞ௨ͷ৔߹͸Τϥʔϋϯυϥʔʹʣ 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() { … } }

Slide 22

Slide 22 text

&SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList() 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) } } }

Slide 23

Slide 23 text

&SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList() 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

Slide 24

Slide 24 text

&SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList() 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) } } }

Slide 25

Slide 25 text

&SSPS)BOEMFS class ErrorHandler { private val queue = LinkedList() 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) } } }

Slide 26

Slide 26 text

&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) } } }

Slide 27

Slide 27 text

&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(…) {…} … }

Slide 28

Slide 28 text

&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(…) {…} … }

Slide 29

Slide 29 text

&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) } } }

Slide 30

Slide 30 text

·ͱΊ

Slide 31

Slide 31 text

·ͱΊ w ΤϥʔΛચ͍ग़ͯ͠ΧςΰϥΠζ͠Α͏ w ॲཧͷ࢓ํΛચ͍ग़ͦ͏ w ΤϥʔͱॲཧΛϚοϐϯά͠Α͏ w ΞϓϦ͸Ϣʔβʔ͕৮Δ΋ͷͳͷͰɺϢʔβʔϏϦςΟ΋ߟ͑Α͏ w ࣌ʹ͸$SBTIͤ͞Δ΂͖ͱ͍͏൑அ΋͠Α͏