我在我的应用中使用“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);
}
}
}
}
答案 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 :(得分: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
,它将始终返回用户对象。