13

Tôi đang sử dụng "Đăng nhập bằng Google" trong ứng dụng của mình. Do đó tôi sử dụng lớp GoogleApiClient để nhận email người dùng và mã thông báo ID mà tôi cần cho chương trình phụ trợ của mình.Đăng nhập im lặng để truy xuất mã thông báo với GoogleApiClient

Khi người dùng đăng nhập, sau đó tôi có quyền truy cập vào một hoạt động (tất nhiên) và tôi sử dụng Hoạt động mà để cho các GoogleApiClient xử lý các công cụ UI vòng đời bằng cách gọi builder.enableAutoManage (myActivity, ...)

Điều này hoạt động tốt.

Tuy nhiên, ở giai đoạn sau (vài ngày sau), tôi cần lấy mã thông báo mới (vì một lý do nào đó tôi sẽ không đi sâu vào đây). Tôi muốn nhận được mã thông báo này âm thầm mà không có sự tương tác của người dùng. Tuy nhiên, tại thời điểm trong mã của tôi, nơi tôi cần mã thông báo mới này, tôi không có quyền truy cập vào bất kỳ cá thể Hoạt động nào. Điều đó có nghĩa là tôi không thể để thực hiện cuộc gọi được đề cập ở trên, tức là "builder.enableAutoManage". Và tôi đã thấy rằng nếu tôi không thực hiện cuộc gọi đó, thì thông tin đăng nhập im lặng dường như không hoạt động.

Tôi đã đính kèm mã bên dưới. Bây giờ, hãy xem phương thức "silentLogin". Miễn là mã thông báo mà tôi đã nhận được khi người dùng đã đăng nhập thực tế, nhỏ hơn một giờ, thì câu lệnh "pendingResult.isDone" sẽ trả về true và có thể nhận được mã thông báo được lưu trong bộ nhớ cache. Tuy nhiên, nếu mã thông báo mà tôi đã nhận được khi người dùng đã đăng nhập thực tế, cũ hơn một giờ, thì cuộc gọi "pendingResult.setResultCallback" được thực hiện NHƯNG phương thức "onResult" KHÔNG BAO GIỜ ĐƯỢC GỌI và tôi không thể nhận được thông tin mới mã thông báo. Vấn đề này không xảy ra nếu tôi làm chính xác như vậy từ một hoạt động (và bằng cách đó cũng thực hiện cuộc gọi "builder.enableAutoManage"). Vì vậy, không ai biết những gì tôi đang làm sai và quan trọng hơn - làm thế nào để giải quyết vấn đề này và nhận được một mã thông báo mới mà không cần truy cập vào một thể hiện hoạt động? Không.

Tôi đang sử dụng com.google.android.gms: play-dịch vụ-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

Khi bạn nói mã thông báo, tôi có thể hỏi loại mã thông báo nào không? – zIronManBox

+0

Mã thông báo mà tôi muốn từ Google API là mã thông báo ID (Tôi nghĩ đó là loại mã thông báo duy nhất mà bạn có thể nhận được bằng cách gọi GoogleSignInAccount.getIdToken()) –

Trả lời

8

tôi thấy vấn đề. Tôi đã theo ấn tượng rằng chức năng

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

đang diễn ra để kết nối các mGoogleApiClient cho tôi (vì nó trả về một kết quả cấp phát). Tuy nhiên, đó không phải là trường hợp và để giải quyết vấn đề trên, tôi chỉ cần thêm cuộc gọi

ConnectionResult result = mGoogleApiClient.blockingConnect(); 

vào đầu phương thức im lặngLogin. (Và sau đó dĩ nhiên ngắt kết nối sau này, và cũng đảm bảo cuộc gọi được thực hiện trong một chủ đề khác nhau từ chủ đề chính)

tada'

22

Vâng, trả lời trên là chính xác. Nói chung, mọi GoogleApiClient cần được kết nối trước khi có thể trả lại cho bạn bất kỳ dữ liệu nào. enableAutoManage giúp bạn gọi connect()/disconnect() tự động trong khi onStart()/onStop(). Nếu bạn không sử dụng autoManage, bạn sẽ cần phải kết nối() theo cách thủ công.

Và thậm chí tốt hơn, bạn nên ngắt kết nối trong một khối cuối cùng.

Giả sử bạn không ở trên chuỗi giao diện người dùng.

try { 
    ConnectionResult result = mGoogleApiClient.blockingConnect(); 
    if (result.isSuccess()) { 
     GoogleSignInResult googleSignInResult = 
      Auth.GoogleSignInApi.silentSignIn(googleApiClient).await(); 
    ... 
    } 
} finally { 
    mGoogleApiClient.disconnect(); 
} 

Đồng thời, để làm sạch mã của bạn một chút: 1.gso xây dựng từ bên dưới cấu hình giống hệt mã dán của bạn ở trên:

GoogleSignInOptions gso = 
    new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) 
     .requestIdToken(SERVER_CLIENT_ID) 
     .requestEmail() 
     .build(); 
  1. Dựa trên logic hiện tại của bạn, addOnConnectionFailedListener/addConnectionCallbacks không giúp khác hơn log adb. Có lẽ chỉ cần loại bỏ chúng hoàn toàn?
+0

Cảm ơn bạn Isabella đã trả lời! –

+3

Tài liệu google luôn thất bại một cách ngoạn mục. Nó là hoàn toàn không rõ ràng nếu apiClient.connect nên được gọi trước hoặc sau khi một dấu hiệu đã cố gắng. Trong thực tế, tôi nghĩ rằng mô hình mới của tách ra hát trong và kết nối đã tăng độ phức tạp, không giảm nó. Dù sao thì, cảm ơn Isabella vì câu trả lời, điều này cũng đã giúp tôi! – Creos

+0

Tôi nghĩ rằng đó là một tuyên bố khá táo bạo khi nói rằng tài liệu của Google luôn thất bại một cách ngoạn mục. – rupps

Các vấn đề liên quan