[英]Google Sign-In: how to retry when there is a failure?
Issue: I need to refresh the Google token used for signing into my server. 问题:我需要刷新用于登录服务器的Google令牌。 Most of the time this works well, but sometimes the call to Google to get a fresh token (with a TTL of ~1hr) fails for a variety of reasons.
在大多数情况下,此方法运行良好,但有时由于多种原因,致电Google以获得新令牌(TTL为〜1hr)失败。
Desired solution: some means of retrying the call to Google that will actually work. 所需的解决方案:可以重试对Google的呼叫的一些方法,这些方法实际上是有效的。
I have code like the following in my app: 我的应用中有类似以下的代码:
private val googleSignInClient: GoogleSignInClient by lazy {
// This takes a measurable amount of time to compute, so do it lazily
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(WEB_CLIENT_ID) // need this to get user ID token later
.requestEmail()
.build()
GoogleSignIn.getClient(appContext, gso)
}
override fun getToken() = getRefreshedGoogleInfo()?.googleToken()
/**
* Here we "silently sign in" to get a refreshed Google ID Token.
*
* This method might block, so do not call it from the main thread.
*/
private fun getRefreshedGoogleInfo(): GoogleUserInfo? {
val task = googleSignInClient.silentSignIn()
// If the task is already complete, return the result immediately
if (task.isComplete) {
val info = task.result.toGoogleUserInfo()
Logger.v(TAG, "silentSignIn result from already-completed task = %s", info.toString())
return info
}
// If the task is not complete, await up to 5s and return result, or null
return try {
val info = task.await().toGoogleUserInfo()
Logger.v(TAG, "silentSignIn result from await task = %s", info.toString())
info
} catch (e: Exception) {
Logger.e(TAG, e, "silentSignIn result from await task = null\nerror = ${e.localizedMessage}")
null
}
}
private fun Task<GoogleSignInAccount>.await() = Tasks.await(this, 5, TimeUnit.SECONDS)
Sometimes, the call task.await()
will fail because it timed out. 有时,调用
task.await()
会因超时而失败。 In such a case, what is the best strategy to try again? 在这种情况下,再试一次的最佳策略是什么? I have tried a naive strategy of just trying again immediately up to some arbitrary numerical limit, but I have observed that if it fails the first time, it always fails on subsequent attempts.
我尝试过一种天真的策略,即立即尝试再次达到某个任意的数值限制,但是我观察到,如果它第一次失败,则在以后的尝试中总是失败。 The Google docs aren't very helpful with respect to this scenario.
Google 文档在这种情况下不是很有帮助。
Instead of waiting the task up to 5 seconds, why not try again with requests and intents ? 与其等待任务长达5秒钟,不如尝试使用请求和意图重试? Let me show you in your code with edits.
让我在您的代码中通过修改向您展示。
private fun getRefreshedGoogleInfo(): GoogleUserInfo? {
val task = googleSignInClient.silentSignIn()
// If the task is already complete, return the result immediately
if (task.isComplete) {
val info = task.result.toGoogleUserInfo()
Logger.v(TAG, "silentSignIn result from already-completed task = %s", info.toString())
return info
}
else{ //Else is not necessary, but it will somehow increase readability.
// There's no immediate result ready, displays some progress indicator and waits for the async callback.
task.addOnCompleteListener(this){ signInTask ->
//We repeat the same task control again, but this time it is async.
if(signInTask.isComplete){
val info = task.result.toGoogleUserInfo() //Redone the same things in first task.isComplete scope.
Logger.v(TAG, "silentSignIn result from already-completed task = %s", info.toString())
return info
}
else{ //Again, not necessary but still.
//This is where we are gonna try again.
signInToGoogle() //Go below to see the trick.
}
}
}
}
With the code above alone, you do not have to wait for 5 seconds, but it will not try again. 仅使用上面的代码,您不必等待5秒钟,但不会再次尝试。 To try again with more persistant way ( I assume you use these functions on an activity ) :
要以更持久的方式重试(我假设您在活动中使用了这些功能):
private fun signInToGoogle(){
val signInIntent = googleSignInClient!!.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN) //You might consider checking for internet connection before calling this
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == RC_SIGN_IN){
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
/* your code with task handle goes here */
//task.result carries the same object as above. But since this is a listener, it can not be returned with a value. So it is up to you how to handle again. I handle this with a class-wide variable which contains GoogleUserInfo?
}
}
RC_SIGN_IN is just a number for request. RC_SIGN_IN只是一个请求号码。 You could add it to your companion objects like this or use it as it is :
您可以像这样将其添加到您的伴侣对象中,也可以按原样使用它:
companion object {
private const val RC_SIGN_IN = 9001
}
Note : You could use intents for the first try too.
注意 :您也可以在第一次尝试中使用intent。 Or if intents are too much hassle for you, you could Make function sleep for 5 seconds and call the function again.
或者,如果意图对您来说太麻烦了,您可以使函数休眠5秒钟,然后再次调用该函数。
But i strongly recommend you to use intent.
但我强烈建议您使用意图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.