5

próbuję się zalogować do mojej aplikacji przy użyciu GoogleAccountCredential do uwierzytelniania:Niezastosowanie się odzyskać OAuth 2.0 token dostępu na android emulator

mGoogleAccountCredential = GoogleAccountCredential.usingOAuth2(context, Arrays.asList(Scopes.EMAIL, Scopes.PLUS_LOGIN)); 
mGoogleAccountCredential.setSelectedAccountName(accountName); 
String token = mGoogleAccountCredential.getToken(); 

to działa dobrze na prawdziwych urządzeniach, ale na android emulator mGoogleAccountCredential.getToken() nie z następującym wyjątkiem:

java.lang.IllegalArgumentException: the name must not be empty: null 
03-01 19:41:31.604 3203-3361/com.myapp W/System.err:  at android.accounts.Account.<init>(Account.java:48) 
03-01 19:41:31.604 3203-3361/com.myapp W/System.err:  at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source) 
03-01 19:41:31.604 3203-3361/com.myapp W/System.err:  at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:255) 
  • Usługi Google Play obecne na emulatorze (GoogleApiAvailability.isGooglePlayServicesAvailable(context) powraca 0)
  • accountName jest ustawiony i prawidłowe, gdy przeszedł do setSelectedAccountName (ustawiony na "[email protected]")
  • wszystkie uprawnienia, dependecies i konfiguracje istnieć w projekcie (w rzeczywistości, to działa na wszystkich rzeczywistych urządzeń)

Dowolna wskazówka, dlaczego nie działa na emulatorze?

UPD:
Po wykopaniu trochę w kodzie Google: problem występuje w setSelectedAccountName(accountName) metody. Ta metoda prosi o podanie konta związanego z daną nazwą konta GoogleAccountManager. Jeśli nie ma takiego konta, nazwa konta jest ustawiona na null:

public final GoogleAccountCredential setSelectedAccountName(String accountName) { 
    selectedAccount = accountManager.getAccountByName(accountName); 
    // check if account has been deleted 
    this.accountName = selectedAccount == null ? null : accountName; 
    return this; 
    } 

AccountManager z kolei przechodzi nad wszystkimi istniejącego konta i porównuje ich nazw do podanej nazwy konta. Jeśli istnieje dopasowanie, odpowiednie konto jest zwracany:

public Account getAccountByName(String accountName) { 
    if (accountName != null) { 
     for (Account account : getAccounts()) { 
     if (accountName.equals(account.name)) { 
      return account; 
     } 
     } 
    } 
    return null; 
    } 

    public Account[] getAccounts() { 
    return manager.getAccountsByType("com.google"); 
    } 

Chodzi o to, że getAccounts() powraca pusta tablica na emulatorze. Na prawdziwym urządzeniu zwraca jednak właściwą listę.

+0

Czy używasz obrazu systemu interfejsu API Google? – Anthony

+0

Tak, oczywiście, podałem to na moje pytanie. – meltedspark

+0

Po prostu, aby upewnić się (ponieważ nie mam głębokiego doświadczenia w tym temacie), użyłeś "Obrazu Systemu Elementów x86 dla Google API" do zbudowania swojego Wirtualnego Urządzenia, a nie tylko "Element System image x86"? Nie widziałem, gdzie to określono (może pośrednio). – Anthony

Odpowiedz

2

Cóż, jak zawsze wszystko jest łatwiejsze niż się wydaje.
Dzięki this post i b1izzar za wskazanie właściwej odpowiedzi.

Wszystkie rzeczywiste urządzenia, które sprawdziłem, są uruchomione pod numerem Android 5.1 Lollipop.
Wszystkie emulatory, które sprawdziłem, działają pod numerem Android 6.0 Marshmallow.

Na Marshmallow, tj. Na moim emulatorze, nie wystarczy podać w manifeście uprawnienia GET_ACCOUNTS. Jest obowiązkowe żądać tego uprawnienia przy starcie z konkretnych code:

uprawnieniami żądanie:

// Here, thisActivity is the current activity 
if (ContextCompat.checkSelfPermission(thisActivity, 
       Manifest.permission.READ_CONTACTS) 
     != PackageManager.PERMISSION_GRANTED) { 

    // Should we show an explanation? 
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, 
      Manifest.permission.READ_CONTACTS)) { 

     // Show an expanation to the user *asynchronously* -- don't block 
     // this thread waiting for the user's response! After the user 
     // sees the explanation, try again to request the permission. 

    } else { 

     // No explanation needed, we can request the permission. 

     ActivityCompat.requestPermissions(thisActivity, 
       new String[]{Manifest.permission.READ_CONTACTS}, 
       MY_PERMISSIONS_REQUEST_READ_CONTACTS); 

     // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an 
     // app-defined int constant. The callback method gets the 
     // result of the request. 
    } 
} 

Uwaga: w prawoślazuGET_ACCOUNTS, WRITE_CONTACTS i READ_CONTACTS uprawnienia są w tej samej grupie uprawnień, więc raz READ_CONTACTS zostanie przyznany, GET_ACCOUNTS również zostanie przyznany.

Uwaga 2: w Android NugatGET_ACCOUNTS jest deprecated, więc ma to sens, aby korzystać READ_CONTACTS zamiast GET_ACCOUNT nawet w Marshmallow.

1

Być może na emulatorze działa starsza wersja Usług Google. Wygląda na to, że najnowsza wersja rzuciłaby wyjątek GoogleAuthException w przeciwieństwie do wyjątku IllegalArgumentException.

API Doc

public String getToken() 
       throws IOException, 
         com.google.android.gms.auth.GoogleAuthException 
Returns an OAuth 2.0 access token. 
Must be run from a background thread, not the main UI thread. 

Throws: 
IOException 
com.google.android.gms.auth.GoogleAuthException 
+0

IllegalArgumentException to wyjątek środowiska wykonawczego, a nie sprawdzany. Dlatego nie jest to określone w podpisie funkcji. – meltedspark

+0

Masz rację, derp. –

1

Myślę, że problemem jest to, że należy użyć urządzenia fizycznego rozwoju i testowania, ponieważ Google Play nie może być zainstalowany na emulatorze.

Nie widzę innego powodu, ale tutaj masz code snippet zaczerpnięte z tasks-android-sample że również użyć GoogleAccountCredential.

+0

Używam obrazu, na którym są zainstalowane Usługi Google Play ("Obraz systemu interfejsu API Google"). W rzeczywistości mogę zalogować się do mojego konta Gmail na emulatorze. – meltedspark