Erreur dans le fragment: «Vous gérez déjà un GoogleApiClient avec l’ID 0»

Tout fonctionne bien la première fois, si vous lancez une seconde fois, vous voyez cette erreur:

FATAL EXCEPTION: main Process: ro.vrt.videoplayerstreaming, PID: 23662 java.lang.IllegalStateException: Already managing a GoogleApiClient with id 0 at com.google.android.gms.common.internal.zzx.zza(Unknown Source) at com.google.android.gms.common.api.internal.zzw.zza(Unknown Source) at com.google.android.gms.common.api.GoogleApiClient$Builder.zza(Unknown Source) at com.google.android.gms.common.api.GoogleApiClient$Builder.zze(Unknown Source) at com.google.android.gms.common.api.GoogleApiClient$Builder.build(Unknown Source) at ro.vrt.videoplayerstreaming.Login.onCreateView(Login.java:75) at android.support.v4.app.Fragment.performCreateView(Fragment.java:1974) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1252) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617) at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5849) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:763) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:653) 

Voici mon code:

 public class Login extends Fragment implements GoogleApiClient.OnConnectionFailedListener, View.OnClickListener { private static final Ssortingng TAG = "SignInActivity"; private static final int RC_SIGN_IN = 9001; private GoogleApiClient mGoogleApiClient; private TextView mStatusTextView; private ProgressDialog mProgressDialog; private static Ssortingng url; private static View view; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (view != null) { ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) parent.removeView(view); } try { view = inflater.inflate(R.layout.activity_login, container, false); // Views mStatusTextView = (TextView) view.findViewById(R.id.status); // Button listeners view.findViewById(R.id.sign_in_button).setOnClickListener(this); view.findViewById(R.id.sign_out_button).setOnClickListener(this); view.findViewById(R.id.disconnect_button).setOnClickListener(this); // [START configure_signin] // Configure sign-in to request the user's ID, email address, and basic // profile. ID and basic profile are included in DEFAULT_SIGN_IN. GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestEmail() .build(); // [END configure_signin] // [START build_client] // Build a GoogleApiClient with access to the Google Sign-In API and the // options specified by gso. mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity()/* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); // [END build_client] // [START customize_button] // Customize sign-in button. The sign-in button can be displayed in // multiple sizes and color schemes. It can also be contextually // rendered based on the requested scopes. For example. a red button may // be displayed when Google+ scopes are requested, but a white button // may be displayed when only basic profile is requested. Try adding the // Scopes.PLUS_LOGIN scope to the GoogleSignInOptions to see the // difference. SignInButton signInButton = (SignInButton) view.findViewById(R.id.sign_in_button); signInButton.setSize(SignInButton.SIZE_STANDARD); signInButton.setScopes(gso.getScopeArray()); // [END customize_button] } catch (InflateException e) { /* map is already there, just return view as it is */ } super.onCreate(savedInstanceState); return view; } @Override public void onStart() { super.onStart(); OptionalPendingResult opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient); if (opr.isDone()) { // If the user's cached credentials are valid, the OptionalPendingResult will be "done" // and the GoogleSignInResult will be available instantly. Log.d(TAG, "Got cached sign-in"); GoogleSignInResult result = opr.get(); handleSignInResult(result); } else { // 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. showProgressDialog(); opr.setResultCallback(new ResultCallback() { @Override public void onResult(GoogleSignInResult googleSignInResult) { //adaugat de mine sa porneacsa singur cererea de logare signIn(); //fin hideProgressDialog(); handleSignInResult(googleSignInResult); } }); } } // [START onActivityResult] @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); if (requestCode == RC_SIGN_IN) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); handleSignInResult(result); } } // [END onActivityResult] // [START handleSignInResult] private void handleSignInResult(GoogleSignInResult result) { Log.d(TAG, "handleSignInResult:" + result.isSuccess()); if (result.isSuccess()) { // Signed in successfully, show authenticated UI. GoogleSignInAccount acct = result.getSignInAccount(); mStatusTextView.setText(getSsortingng(R.ssortingng.signed_in_fmt, acct.getDisplayName() + " Your token " + acct.getId())); url = "http://grupovrt.ddns.net:81/index.php?token="+acct.getId(); updateUI(true); } else { // Signed out, show unauthenticated UI. updateUI(false); } } // [END handleSignInResult] // [START signIn] private void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_SIGN_IN); } // [END signIn] // [START signOut] private void signOut() { Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback( new ResultCallback() { @Override public void onResult(Status status) { // [START_EXCLUDE] updateUI(false); // [END_EXCLUDE] } }); } // [END signOut] // [START revokeAccess] private void revokeAccess() { Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback( new ResultCallback() { @Override public void onResult(Status status) { // [START_EXCLUDE] updateUI(false); // [END_EXCLUDE] } }); } // [END revokeAccess] @Override public void onConnectionFailed(ConnectionResult connectionResult) { // An unresolvable error has occurred and Google APIs (including Sign-In) will not // be available. Log.d(TAG, "onConnectionFailed:" + connectionResult); } private void showProgressDialog() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(getActivity()); mProgressDialog.setMessage(getSsortingng(R.ssortingng.loading)); mProgressDialog.setIndeterminate(true); } mProgressDialog.show(); } private void hideProgressDialog() { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.hide(); } } private void updateUI(boolean signedIn) { if (signedIn) { getView().findViewById(R.id.sign_in_button).setVisibility(View.GONE); getView().findViewById(R.id.sign_out_and_disconnect).setVisibility(View.VISIBLE); } else { mStatusTextView.setText(R.ssortingng.signed_out); getView().findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE); getView().findViewById(R.id.sign_out_and_disconnect).setVisibility(View.GONE); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.sign_in_button: signIn(); break; case R.id.sign_out_button: signOut(); break; case R.id.disconnect_button: revokeAccess(); break; } } } 

Si je comprends bien, le problème réside dans ces lignes:

  mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity()/* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); 

J’ai essayé de passer explicitement un identifiant de 0 :

 .enableAutoManage(getActivity() /* FragmentActivity */, 0, this /* OnConnectionFailedListener */) 

mais cela n’a toujours pas fonctionné.

Qu’est-ce que je rate?

Vous devez appeler stopAutoManage() dans la méthode onPause() de votre Fragment :

 @Override public void onPause() { super.onPause(); mGoogleClient.stopAutoManage(getActivity()); mGoogleClient.disconnect(); } 

Vous devriez appeler stopAutoManage() dans la méthode onPause() de votre Fragment comme ceci:

 @Override public void onPause() { super.onPause(); mGoogleApiClient.stopAutoManage(getActivity()); mGoogleApiClient.disconnect(); } 

Pour éviter d’autres problèmes

 @Override public void onStop() { super.onStop(); if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { mGoogleApiClient.stopAutoManage((Activity) context); mGoogleApiClient.disconnect(); } } 

J’ai rencontré un problème similaire lorsque j’ai placé un bouton de connexion dans deux Fragment différents appartenant à la même Activity .

J’ai résolu ce problème en atsortingbuant des identifiants différents à chaque GoogleApiClient géré GoogleApiClient .

Par exemple, dans le Fragment 1, lors de la création de mon object GoogleApiClient , j’ai assigné 0 comme identifiant:

 mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity(), 0, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); 

Dans le Fragment 2, lors de la création de mon object GoogleApiClient , j’ai atsortingbué 1 comme identifiant:

 mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) .enableAutoManage(getActivity(), 1, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); 

La doc officielle pour enableAutoManage dit ceci:

À tout moment, un seul client géré automatiquement est autorisé par identifiant. Pour réutiliser un identifiant, vous devez d’abord appeler stopAutoManage (FragmentActivity) sur le client précédent.

Votre code utilise la version de enableAutoManage sans paramètre clientId, il est donc par défaut à 0. Ci-dessous, j’explique pourquoi vous aurez plusieurs clients à gestion automatique pour clientId 0, ce que la documentation ci-dessus met en garde.

Une fois votre fragment de connexion attaché à FragmentActivity, il indique à cette activité de commencer à gérer une nouvelle instance de GoogleApiClient. Mais que se passe-t-il si FragmentActivity gère déjà une autre instance de GoogleApiClient? C’est à ce moment que vous obtenez l’erreur.

Quelques scénarios possibles peuvent mener à cette situation multiple-GoogleApiClients-per-FragmentActivity.

  • FragmentActivity possède un autre fragment en plus de la connexion qui crée un GoogleApiClient et demande également à FragmentActivity de le gérer.
  • FragmentActivity lui-même crée un GoogleApiClient et le gère avant que le fragment de connexion ne soit attaché à FragmentActivity.
  • Peut-être que vous ajoutez un fragment de connexion dans une FragmentTransaction et que vous appelez addToBackStack. Ensuite, l’utilisateur appuie sur, puis plus tard, le fragment de connexion est à nouveau attaché. Dans ce cas, les appels importants à la méthode Login Activity sont onCreateView -> onDestroyView -> onCreateView, comme illustré ici:

    diagramme de cycle de vie des fragments

Cela pose problème car le deuxième appel à Login.onCreateView essaie de faire en sorte que FragmentActivity gère un second GoogleApiClient.

Si j’étais vous, j’envisagerais sérieusement de créer le GoogleApiClient dans l’activité plutôt que dans tous les fragments. Vous pouvez ensuite effectuer le travail nécessitant GoogleApiClient dans l’activité ou continuer à le faire en fragment de connexion après avoir obtenu le GoogleApiClient de l’activité comme suit:

 private GoogleApiClient googleApiClient; @Override void onAttach(Activity activity) { super.onAttach(activity); googleApiClient = activity.getGoogleApiClient(); } @Override void onDetach() { super.onDetach(); googleApiClient = null; } 

Je vous suggère d’initialiser votre mGoogleApiClient dans onCreate() au lieu de dans onCreateView() .

Comme indiqué par @vlazzle , onCreateView() peut être appelé plus d’une fois pendant la durée de vie d’une même Activity .

Essayez d’utiliser votre mGoogleApiClient partir de votre activité. Si vous avez déclaré GoogleApiClient sur votre activité, vous ne pouvez pas le déclarer à nouveau sur votre fragment. à la place, réutiliser la variable dans cette activité à partir du fragment

 mGoogleApiClientInFragment = ((Youractivityclass)getActivity()).mGoogleApiClient; 

remplacez YouractivityClass par votre activité de votre fragment et assurez-vous de définir votre champ mGoogleApiClient dans votre activité sur public