更新が完了したRealmの情報が取得できない(更新前の情報が取得される)
Realm + Retrofit + OkHttp を利用して API をコールするアプリを作ろうと設計しています。ログインが成功すると accessToken と refreshToken をサーバーから返却する仕組みにしていて、返却された値を Realm に保存し、次回以降は Realm から accessToken を取得することを考えています。
accessToken には有効期限があり、有効期限が切れた accessToken を用いて API をコールすると 401 エラーが返却され、refreshToken を利用して accessToken を更新する仕様で進めているのですが、 Realm に保存した情報が上手く取得できず、どこに問題があるか教えていただけないでしょうか?
(現象)
- accessToken の有効期限が切れた状態で、Retrofit を用いて 2 つの非同期なリクエストを同時に投げる(リクエストA, B)
- accessToken の有効期限が切れている為 A, B とも 401エラーが返却される
- TokenAuthenticator の authenticate メソッドが A , B とも呼ばれる
- A の updateToken は成功(タイミングによっては B が成功)。Realm の accessToken と refreshToken が更新される。 B の updateToken は A のリクエストが成功した為、失敗する(すでにサーバーが保持している token 情報が更新されている為)
- OkHttp の仕様で B の authenticate 呼ばれ続けるが、Realm 内の token 情報が更新されていても ソース内の ※1 では古い token 情報(【4】で更新される前の情報)が常に返却されてしまう
環境は Kotlin 1.0.4, Realm 2.2.0, OkHttp 3.3.1。ソースの抜粋は以下になります。
・Token.kt
open class Token(
// token 情報は 1件のみ保存すれば良いので id = 1 を固定で指定
@PrimaryKey open var id: Int = 1,
open var accecctoken: String = "",
open var refreshToken: String = "",
) : RealmObject()
・TokenAuthenticator.kt
open class TokenAuthenticator : Authenticator {
// 401 エラーの際に呼び出される
override fun authenticate(route: Route, response: Response): Request? {
// 保存してある Token 情報を取得
val realm = Realm.getDefaultInstance()
val token = realm?.where(Token::class.java)?.findFirst() ?: return null (※1)
realm.close()
// refreshToken を利用して token 更新のリクエストを同期処理で行う
this.updateToken(token.refreshToken)
// updateToken メソッドで token が更新されるので、新しい token をセットする
val newAccessToken = "Bearer ${token.accessToken}"
return response.request().newBuilder().header("www-Authorization", newAccessToken)?.build()
}
private fun updateToken(refreshToken: String) {
val call = // retrofit で token を取得する call を設定
// retrofit で新しい Token を取得する。取得に失敗したら return する
val newToken: Token? = call.execute().body() ?: return
// 取得した結果を Realm に保存する
Realm.getDefaultInstance().use { realm ->
realm.executeTransaction {
realm.copyToRealmOrUpdate(newToken)
}
}
}
}