[android][retrofit] 세션 종료나 토큰 만료 시 처리하기

retrofit으로 api 호출 중 세션 종료나 토큰 만료 시 특정 화면으로 이동하거나 토큰 갱신을 해야할 때 사용한다.

세션 종료 시 로그인 화면으로 이동하기

// 세션 종료 시 로그인 화면으로 이동하기
class SessionAuthenticator : Authenticator {
  override fun authenticate(route: Route?, response: Response): Request? {
    if (response.code == HttpURLConnection.HTTP_UNAUTHORIZED
      && !SessionExpiredManager.isExpired
      && App.instance.currentActivity !is SignInActivity
    ) {
      SessionExpiredManager.isExpired = true

      Intent(App.instance, SignInActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or
          Intent.FLAG_ACTIVITY_CLEAR_TASK or
          Intent.FLAG_ACTIVITY_NEW_TASK

        putExtra(SignInActivity.SHOW_SESSION_EXPIRED_MESSAGE, true)
      }.also { App.instance.startActivity(it) }
    }
    return null
  }
}

if 에 조건이 많은 건, 동시에 여러 api의 호출결과를 받아도 로그인 화면은 한 번만 호출을 하기위해서다. App.kt 클래스는 http://susemi99.kr/5149 에 있고, SessionExpiredManager.isExpired 는 object 클래스에 변수만 하나 딸랑 있는거다.

토큰 만료 시 갱신하기

class TokenAuthenticator : Authenticator {
  override fun authenticate(route: Route?, response: Response): Request? {
    if (response.code == HttpURLConnection.HTTP_UNAUTHORIZED) {
      if (AppPreference.refreshToken.isNotEmpty()) {
        val refreshTokenResponse = RefreshTokenService.refresh(AppPreference.refreshToken).execute()
        if (handleResponse(refreshTokenResponse))
          return makeRequest(response)
      }
    }
    return null
  }

  private fun handleResponse(refreshTokenResponse: retrofit2.Response<Token>): Boolean {
    return if (refreshTokenResponse.isSuccessful && refreshTokenResponse.body() != null) {
      AppPreference.saveToken(refreshTokenResponse.body()!!)
      true
    } else {
      UserRepository.instance.logout()
      false
    }
  }

  private fun makeRequest(response: Response): Request {
    return response.request
      .newBuilder()
      .removeHeader("Authorization")
      .addHeader("Authorization", AppPreference.accessToken)
      .build()
  }
}

dispose() 를 할 수 없으니 다른 api는 RxJava를 사용하더라도 RefreshTokenService.refresh()는 retrofit의 기본인 Call<Any> 을 사용하는게 좋다.