web-dev-qa-db-fra.com

Erreur dans le fragment: "Gestion déjà d'un GoogleApiClient avec l'ID 0"

Tout fonctionne correctement la première fois, si vous lancez une deuxième 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 String TAG = "SignInActivity";
    private static final int RC_SIGN_IN = 9001;

    private GoogleApiClient mGoogleApiClient;
    private TextView mStatusTextView;
    private ProgressDialog mProgressDialog;

    private static String 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<GoogleSignInResult> 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<GoogleSignInResult>() {
                @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(getString(R.string.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<Status>() {
                    @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<Status>() {
                    @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(getString(R.string.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.string.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 est dans ces lignes:

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

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

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

mais cela n'a toujours pas fonctionné.

Qu'est-ce que je rate?

56
diaconu liviu

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

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

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();
}
32
Adewale Balogun

Pour éviter d'autres problèmes

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

J'ai rencontré un problème similaire lorsque j'ai placé un bouton de connexion dans deux Fragments différents appartenant au même Activity.

J'ai résolu ce problème en affectant différents identifiants à chaque GoogleApiClient géré automatiquement.

Par exemple, dans Fragment 1, lors de la création de mon objet GoogleApiClient, je ai assigné 0 en tant qu'id:

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

Dans Fragment 2, lors de la création de mon objet GoogleApiClient, je ai affecté 1 en tant qu'id:

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

Le doc officiel de 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. Par conséquent, la valeur par défaut est 0. Ci-dessous, nous expliquons pourquoi vous aurez plusieurs clients gérés automatiquement pour clientId 0, ce que la documentation ci-dessus met en garde.

Une fois que votre fragment de connexion est associé à une 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-là que vous obtenez l'erreur.

Il existe quelques scénarios possibles pouvant mener à cette situation liée à plusieurs GoogleApiClients-per-FragmentActivity.

  • FragmentActivity a un autre fragment, en plus de Login, qui crée un GoogleApiClient et demande également à FragmentActivity de le gérer.
  • FragmentActivity lui-même crée un GoogleApiClient et commence à le gérer avant que le fragment de connexion ne soit associé à FragmentActivity.
  • Peut-être ajoutez-vous Fragment de connexion à une FragmentTransaction et appelez addToBackStack. Ensuite, l'utilisateur appuie en arrière, puis plus tard, le fragment de connexion est réattaché. Dans ce cas, les appels de méthode importants pour l'activité de connexion sont onCreateView -> onDestroyView -> onCreateView, comme indiqué ici:

     fragment lifecycle diagram

Cela est problématique car le deuxième appel à Login.onCreateView tente de faire en sorte que FragmentActivity gère un deuxième GoogleApiClient.

Si j'étais vous, je penserais sérieusement à créer le GoogleApiClient dans l'activité plutôt que dans des fragments. Vous pouvez ensuite faire le travail nécessitant GoogleApiClient dans l'activité ou continuer à le faire dans le fragment de connexion après avoir récupéré 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;
}
5
vlazzle

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

Comme indiqué par @vlazzle , onCreateView() peut être appelé plusieurs fois au cours de la vie d'une seule variable Activity.

1
pauminku

Vous devriez appeler stopAutoManage() dans la méthode onDestroy() de votre fragment de la manière suivante: 

     @Override
        public void onDestroy() {
             super.onDestroy();
             mGoogleApiClient.stopAutoManage(getActivity());
             mGoogleApiClient.disconnect();
          }
0
Kishan Thakkar