概要
OkHttp3のAuthenticatorを使って再認証処理を書いている場合に、Retrofit2のRx実装からCoroutine実装に変更すると認証処理が詰まってそれ以後のすべての通信ができなくなることがあります。
変更内容
変更前
interface Api {
@Post("authorize")
fun authorize(): Single<Token>
}
class TokenRefreshAuthenticator @Inject constructor(
private val api: Api
): Authenticator {
override fun authenticate(route: Route?, response: Response): Request?
val token = api.authorize().blockingGet()
// Tokenを使った処理
}
}
変更後
interface Api {
@Post("authorize")
suspend fun authorize(): Token
}
class TokenRefreshAuthenticator @Inject constructor(
private val api: Api
): Authenticator {
override fun authenticate(route: Route?, response: Response): Request?
val token = runBlocking { api.authorize() }
// Tokenを使った処理
}
}
対処法
単純なのはCoroutine実装を使うのをやめてCallで直接呼びましょう
interface Api {
@Post("authorize")
fun authorize(): Call<Token>
}
class TokenRefreshAuthenticator @Inject constructor(
private val api: Api
): Authenticator {
override fun authenticate(route: Route?, response: Response): Request?
val token = api.authorize().execute().body()
// Tokenを使った処理
}
}
原因
Retrofit2のRx実装とCoroutine実装の違いにあります。Rx実装では同時呼び出しに上限がありませんが、Coroutine実装には同時実装の呼び出し上限があり、認証切れ時に大量のAPI呼び出しを行うと再認証処理が同時に走り再認証待ちとAPIの処理待ちのデッドロック状態になってしまいます。
ちょっとした変更なのに詰まるっていうのはどうかと思いますが、ハマると辛いので誰かの役に立つと嬉しいです。