Slide 1

Slide 1 text

try-catchからrunCatchingに 移行した話 安齋祐紀 (@off2white)

Slide 2

Slide 2 text

自己紹介 安齋祐紀(あんざいゆうき) Twitter: @off2white 株式会社 ディー・エヌ・エー(DeNA) - 次世代タクシー配車サービス「 MOV」 - Androidアプリ開発担当 - プロジェクト管理とコーディングの割合 = 50:50 (気持ちは) 最近の悩み いまだに運営さんが採択時に 人を間違えていなかったのか心配

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

背景 個人的エラーハンドリング の変遷

Slide 5

Slide 5 text

その1
 むやみに例外を使わなくなった

Slide 6

Slide 6 text

ClassA ClassB ClassC Exception New ClassD 誰が Catch する? NewException Not For Me Exception ?

Slide 7

Slide 7 text

ClassA ClassB ClassC sealed class New ClassD I Like sealed class sealed class

Slide 8

Slide 8 text

ViewModel Repository Api Exception New DB ただし現実的にはこんな感じ sealed class Exception

Slide 9

Slide 9 text

その2
 Crashlyticsの発達によって 細かくExceptionをCatchする必要が なくなった

Slide 10

Slide 10 text

蘇る悪夢 try {
 response = apiRequest.execute() 
 dao.save(response.toEntity())
 
 } catch (e: IOException) {
 errorCode += “01”
 throw NetworkException(errorCode) 
 
 } catch (e: SQLException) {
 errorCode += “03”
 throw GeneralException(errorCode) 
 
 } catch (e: Throwable) {
 errorCode += “04”
 throw SystemException(errorCode) 
 
 }


Slide 11

Slide 11 text

その3
 Rx -> Coroutineへの移行

Slide 12

Slide 12 text

Rx では success と error 時の処理を 分けて記載できた repository.fetchData()
 
 .subscribeBy (
 onNext = { livedata.postValue(it)},
 
 onError = { Timber.e(it) }
 )
 実 行 系 正 常 系 異 常 系

Slide 13

Slide 13 text

もっと良いエラーハンドリングの書き方はないのか


Slide 14

Slide 14 text

runCatching (Kotlin 1.3)


Slide 15

Slide 15 text

runCatching {
 apiRequest.execute()
 } 
 
 or 
 
 apiRequest.execute()
 .runCatching {
 dao.save(it.toEntity())
 } 
 こんな感じで書く

Slide 16

Slide 16 text


 runCatchingは Resultクラスを返却する

Slide 17

Slide 17 text

成功結果と例外を カプセル化してくれる

Slide 18

Slide 18 text

val result = runCatching {
 apiRequest.execute()
 }
 
 if (result.isFailure) {
 Timber.e(
 result.exceptionOrNull()
 )
 return
 }
 処理結果を受け取って 返却してくれる

Slide 19

Slide 19 text

runCatching {
 apiRequest.execute()
 }.onSuccess {
 dao.save(it.toEntity())
 }.onFailure {
 Timber.e(it)
 }
 成功処理と失敗処理を 分けて記載できる 実 行 系 正 常 系 異 常 系

Slide 20

Slide 20 text

runCatching {
 apiRequest.execute()
 }.mapCatching {
 dao.save(it.toEntity())
 }.onSucess {
 …
 }.onFailure {
 ...
 }
 map 時も Catch できる

Slide 21

Slide 21 text

runCatching {
 apiRequest.execute()
 }.recoverCatching {
 Response.default()
 }
 例外処理のリカバリ処 理も綺麗にかける

Slide 22

Slide 22 text


 これは是非使うべき!!!

Slide 23

Slide 23 text

結論
 案外不評


Slide 24

Slide 24 text

try {
 res = apiClient.execute()
 dao.save(res.toEntity())
 }
 catch (e: Exception) {
 Timber.e(e)
 } 
 正常パスと 例外処理で 別れている方が 好みの人もいる 正 常 パ ス 例 外 処 理 理由その1


Slide 25

Slide 25 text

val a: Int? = 
 try { parseInt(input) } catch (e: Exception) 
 { null }
 Kotlin の try-catch は式 として書けるので それで十分説 理由その2


Slide 26

Slide 26 text

val a: Int? = 
 try { parseInt(input) } catch (e: Exception) 
 { null }
 finally { ... }
 try-catch なら finally で 明示的に書ける (runCatching ではできない ) 理由その3


Slide 27

Slide 27 text

return runCatching {
 …
 }
 Result 型は return できない 理由その4


Slide 28

Slide 28 text

fun function() : Int {
 runCatching {
 "5".toInt()
 }.onSuccess {
 return@function it
 }.onFailure {
 return@function 0
 }
 // ここにreturnが必要
 }
 a ‘return’ expression required in a function with a block body 理由その5


Slide 29

Slide 29 text

(;Ծ﹏Ծ)ぐぬぬ


Slide 30

Slide 30 text

runCatching {
 runFunction()
 }.onSuccess {
 dispatch(Action.Success)
 }.onFailure {
 dispatch(Action.Failure)
 }
 現状は return しない ところで そっと使っている ActionCreator の dispatch とか ViewModel の LiveData.postValue とか

Slide 31

Slide 31 text

Resultクラスの
 KEEPでの議論が面白いので
 ぜひ読んでね!