无声登录以使用GoogleApiClient检索令牌

时间:2016-01-20 13:09:23

标签: android google-authentication google-signin googlesigninaccount

我在我的应用中使用“Google登录”。因此,我使用GoogleApiClient类来获取用户电子邮件和我后端所需的ID令牌。

当用户登录时,我可以访问一个Activity(当然),我使用该Activity让GoogleApiClient通过调用来处理UI生命周期内容 builder.enableAutoManage(myActivity,...)

这很好用。

然而,在稍后阶段(几天后),我需要获得一个新令牌(出于某种原因,我不会再进一步​​讨论)。我想得到这个令牌 默默无需用户交互。但是,在我需要这个新令牌的代码中,我无法访问任何Activity实例。这意味着我无法做到 进行上述呼叫,即“builder.enableAutoManage”。而且我发现,如果我不打那个电话,那么静默登录似乎不起作用。

我已附上以下代码。现在,看看“silentLogin”方法。只要我作为用户收到的令牌执行了实际登录,小于一小时,则语句“pendingResult.isDone”将返回true并且可以接收缓存的令牌。但是,如果我作为用户进行实际登录时收到的令牌超过一小时,那么就会调用“pendingResult.setResultCallback”,但是“onResult”方法永远不会被调用,我无法获得新的令牌。如果我从一个活动中做同样的事情(并且通过调用“builder.enableAutoManage”)也不会发生这个问题。

那么,是否有人知道我做错了什么,更重要的是 - 如何解决这个问题并获得一个新的令牌而无法访问活动实例?

我使用的是com.google.android.gms:play-services-auth:8.4.0

package com.google.samples.quickstart.signin;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.OptionalPendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;

/**
 * Use this class to login with google account using the OpenId oauth method.
 */
public class GoogleLoginStackOverflow {
    private static final String TAG = GoogleLoginIdToken.class.getName();
    private static final String SERVER_CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com";

    private GoogleApiClient mGoogleApiClient;
    private Context mContext;

    private GoogleLoginStackOverflow(Context appContext) {
        this.mContext = appContext;
        createGoogleClient();
    }

    /**
     * Performs a silent sign in and fetch a token.
     *
     * @param appContext Application context
     */
    public static void silentLogin(Context appContext) {
        GoogleLoginStackOverflow googleLoginIdToken = new GoogleLoginStackOverflow(appContext);
        googleLoginIdToken.silentLogin();
    }

    private void createGoogleClient() {
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestProfile()
                .requestScopes(new Scope(Scopes.PROFILE))
                .requestIdToken(SERVER_CLIENT_ID)
                .requestEmail()
                .build();

        mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(ConnectionResult connectionResult) {
                        System.out.println("onConnectionFailed  = " + connectionResult);
                        onSilentLoginFinished(null);
                    }
                })
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(Bundle bundle) {
                        System.out.println("onConnected bundle = " + bundle);
                        onSilentLoginFinished(null);
                    }

                    @Override
                    public void onConnectionSuspended(int i) {
                        System.out.println("onConnectionSuspended i = " + i);
                        onSilentLoginFinished(null);
                    }
                }).addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();
    }

    private void silentLogin() {
        OptionalPendingResult<GoogleSignInResult> pendingResult = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
        if (pendingResult != null) {
            if (pendingResult.isDone()) {
                // If the user's cached credentials are valid, the OptionalPendingResult will be "done"
                // and the GoogleSignInResult will be available instantly.
                Log.d(TAG, " ----------------  CACHED SIGN-IN ------------");
                System.out.println("pendingResult is done = ");
                GoogleSignInResult signInResult = pendingResult.get();
                onSilentLoginFinished(signInResult);
            } else {
                System.out.println("Setting result callback");
                // If the user has not previously signed in on this device or the sign-in has expired,
                // this asynchronous branch will attempt to sign in the user silently.  Cross-device
                // single sign-on will occur in this branch.
                pendingResult.setResultCallback(new ResultCallback<GoogleSignInResult>() {
                    @Override
                    public void onResult(GoogleSignInResult googleSignInResult) {
                        System.out.println("googleSignInResult = " + googleSignInResult);
                        onSilentLoginFinished(googleSignInResult);
                    }
                });
            }
        } else {
            onSilentLoginFinished(null);
        }
    }

    private void onSilentLoginFinished(GoogleSignInResult signInResult) {
        System.out.println("GoogleLoginIdToken.onSilentLoginFinished");
        if (signInResult != null) {
            GoogleSignInAccount signInAccount = signInResult.getSignInAccount();
            if (signInAccount != null) {
                String emailAddress = signInAccount.getEmail();
                String token = signInAccount.getIdToken();
                System.out.println("token = " + token);
                System.out.println("emailAddress = " + emailAddress);
            }
        }
    }
}

3 个答案:

答案 0 :(得分:27)

是的,上面的回答是正确的。通常,任何GoogleApiClient都需要先连接才能返回任何数据。 enableAutoManage可以帮助您在onStart()/ onStop()期间自动调用connect()/ disconnect()。如果您不使用autoManage,则需要手动连接()。

更好的是,你应该在finally块中断开连接。

假设您不在UI线程上。

library(dplyr)
library(psd) # Loaded psd (1.0.1) -- Adaptive multitaper spectrum estimation

time <- seq(1,30)
y1 <- sort(runif(30,-0.014,0.014),decreasing=TRUE)
y2 <- sort(runif(30,-0.012,0.012),decreasing=TRUE)
df <- data.frame(y1,y2,time)
#calculation phase difference between two waves y1 and y2  

phase_diff <- function(y1,y2,time){
  out1=pspectrum(y1*(-1),x.frqsamp = 0.1);
  out2=pspectrum(y2*(-1),x.frqsamp = 0.1);
  f1 = out1$freq[which.min(out1$spec)];
  f2 <- out2$freq[which.min(out2$spec)];
  fit1 <- lm(y1 ~ sin(2*pi*f1*time)+cos(2*pi*f1*time));
  fit2 <- lm(y2 ~ sin(2*pi*f2*time)+cos(2*pi*f2*time));
  a1 <- fit1$coefficients[2];
  b1 <- fit1$coefficients[3];
  ph1 <- atan(b1/a1);
  a2 <- fit2$coefficients[2];
  b2 <- fit2$coefficients[3];
  ph2 <- atan(b2/a2);
  phase_difference <- as.numeric((ph2-ph1)/pi);
  return(phase_difference)
}

dff <- df%>%
mutate_each(funs(phase_diff),phase=c(y1,y2,time))

Stage  0 est. (pilot) 
    environment  ** .psdEnv **  refreshed
    detrending (and demeaning)
Stage  1 est. (Ave. S.V.R. -10.9 dB) 
Stage  2 est. (Ave. S.V.R. -8.3 dB) 
Stage  3 est. (Ave. S.V.R. -8.3 dB) 
Stage  4 est. (Ave. S.V.R. -8.3 dB) 
Stage  5 est. (Ave. S.V.R. -8.3 dB) 
Normalized  single-sided  psd estimates ( psd ) for sampling-freq.  0.1
Error: argument "y2" is missing, with no default

此外,还要清理一下代码: 1.从下面的配置构建的gso与上面的粘贴代码相同:

try {
    ConnectionResult result = mGoogleApiClient.blockingConnect();
    if (result.isSuccess()) {
        GoogleSignInResult googleSignInResult =
            Auth.GoogleSignInApi.silentSignIn(googleApiClient).await();
    ...
    }
} finally {
    mGoogleApiClient.disconnect();
}
  1. 根据您当前的逻辑,addOnConnectionFailedListener / addConnectionCallbacks除了adb日志之外没有帮助。也许只是完全删除它们?

答案 1 :(得分:9)

我发现了问题。我的印象是功能

OptionalPendingResult<GoogleSignInResult> pendingResult = Auth.GoogleSignInApi.silentSignIn(googleApiClient);

将为我连接mGoogleApiClient(因为它返回待处理的结果)。然而,事实并非如此,为了解决上述问题,我只需要添加呼叫

ConnectionResult result = mGoogleApiClient.blockingConnect();

在silentLogin方法的开头。 (然后当然会在以后断开连接,并确保在与主线程不同的线程中进行调用)

多田'

答案 2 :(得分:0)

如果要在Firebase中使用lib中的新登录名,请添加到Isabella和Ola的上述两个答案中:

FirebaseAuth.getInstance().currentUser?.let{ 
    //create sign-in options the usual way
    val googleSignInClient = GoogleSignIn.getClient(context, gso)
    googleSignInClient.silentSignIn().addOnCompleteListener {
        val account: GoogleSignInAccount? = it.result
        //get user info from account object
    }
}

此外,这可以从UI线程调用。 如果您曾经登录过FirebaseAuth.getInstance().currentUser,它将始终返回用户对象。