【问题标题】:Getting Firebase user token synchronously同步获取 Firebase 用户令牌
【发布时间】:2018-07-26 19:53:25
【问题描述】:

我正在尝试获取 Firebase 令牌来验证我对 Rest API 的调用。我可以使用以下代码异步生成令牌。

    FirebaseUser mUser = App.getFirebaseAuth().getCurrentUser();
    if (mUser!=null) {
        mUser.getIdToken(false)
                .addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
                    public void onComplete(@NonNull Task<GetTokenResult> task) {
                        if (task.isSuccessful()) {
                            ID_TOKEN = task.getResult().getToken();
                        } else {
                            Log.e(App.TAG, "Firebase Token task ended with error.");
                        }
                    }
                });
    } else {
        Log.i(App.TAG,"User is null, no Firebase Token available");
    }

ID_TOKEN 是一个保存结果的静态字符串变量。 问题是,我正在构建我的请求并添加身份验证标头。

        headers.put("Authentication",
                "Bearer  + ID_TOKEN);

问题是,由于 Firebase 令牌是异步检索的,因此 ID_TOKEN 变量有时为空。我尝试使用

强制线程等待任务

Tasks.await(任务)

但是我得到一个异常,说不能在主线程中调用 await。

有没有其他方法可以同步获取token,或者让线程等到任务完成?

【问题讨论】:

  • 你可以尝试使用信号量让主线程等待。但是如果在主线程上调用回调,它将不起作用。可能根本不建议这样做。

标签: android firebase firebase-authentication


【解决方案1】:

我现在就是这样:

private suspend fun getTokenResult (firebaseUser: FirebaseUser) = suspendCoroutine<GetTokenResult?> { continuation ->
firebaseUser.getIdToken(true).addOnCompleteListener {
    if (it.isSuccessful) {
        continuation.resume(it.result)
    } else {
        continuation.resume(null)
    }
}

使用挂起函数和延续机制。所以如果你使用Coroutines,这可能是最简单的方法

【讨论】:

    【解决方案2】:

    我已经做到了,而且效果很好。 这是主类:

    object FcmToken {
        @JvmStatic
        fun getToken(): String? {
            val task = FirebaseInstanceId.getInstance().instanceId
            try {
                val result = Tasks.await(task)
                return result.token
            } catch (e: ExecutionException) {
                throw IllegalArgumentException("")
            } catch (e: InterruptedException) {
                throw IllegalArgumentException("")
            }
        }
    }
    

    在视图模型中。

    viewModelScope.launch(Dispatchers.IO) {
            val result = FcmToken.getToken(viewModelScope)
            Log.e("result", result.toString())
        }
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,当我得到 407 时,我需要在 Retrofit Authenticator 中更新令牌。我使用 CountDownLatch

      override fun authenticate(route: Route?, response: Response): Request? {
          val user = FirebaseAuth.getInstance().currentUser
          var token: String? = null
          val lock = CountDownLatch(1)
      
          user?.getIdToken(true)?.addOnCompleteListener { task ->
              if (task.isSuccessful) {
                  token = task.result?.token
                  if (token == null) {
                      lock.countDown()            //<--unlock 
                      return@addOnCompleteListener
                  }
                  //save token
              } else {
                  lock.countDown()                //<--unlock 
                  return@addOnCompleteListener
              }
          }
          lock.await()                            //<--wait unlock 
      
          return if (token != null)
              response.request().newBuilder().header(AUTHORIZATION_KEY, token).build()
          else null
      }
      

      【讨论】:

        【解决方案4】:

        如果可能,只需将您的所有后续操作(如 headers.put(...))移至 onComplete 块中。这将保证执行顺序。

        【讨论】:

          【解决方案5】:

          强制在主线程上同步运行是一个非常糟糕的主意。它可能会锁定您的应用程序并可能导致其 ANR。这就是为什么 Android 在您尝试运行 Tasks.await() 时会抱怨的原因 - 它知道可能存在问题。

          学习如何进行异步编程以正确方式执行此操作要好得多。您必须在必须提供的侦听器中接收令牌。

          Please read this blog 详细了解 Firebase API 为何是异步的。

          【讨论】:

          • 谢谢!将阅读博客,看看我是否可以重新编写我的代码。
          • 如果我们想在可以手动编写的后台线程上同步获取令牌怎么办?
          • @Vincent_Paing Android 上所有需要 I/O 的 Firebase API 都是异步的,没有例外。
          • 我只是将它包装在 RxJava 中并使用 .blockingGet 来获取令牌。这很hacky,但我猜它可以工作
          • @DougStevenson 这只是假设您的 api 客户端的开发人员没有经验。任何 API 都应该首先提供同步访问,因为客户端可以拥有自己的异步访问方式。想想 rxjava,它封装一个同步 api 比 async 更干净。您仍然可以通过博客为没有经验的开发人员推广自定义的异步访问方式(任务),但不提供任何同步方式是一种糟糕的 api 设计恕我直言。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-10-29
          • 1970-01-01
          • 2023-04-06
          • 2021-12-08
          • 2018-04-25
          • 2013-04-06
          • 2016-09-24
          相关资源
          最近更新 更多