简体   繁体   English

Google登录:出现故障时如何重试?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM