wake-up-neo.com

Fehler im Fragment: "GoogleApiClient mit der ID 0 bereits verwalten"

Beim ersten Start funktioniert alles richtig, wenn Sie ein zweites Mal starten, wird dieser Fehler angezeigt

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)

Hier ist mein 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;
        }
    }
}

Nach meinem Verständnis liegt das Problem in diesen Zeilen:

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

Ich habe versucht, explizit eine ID von 0 zu übergeben:

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

aber das hat immer noch nicht funktioniert.

Was vermisse ich?

56
diaconu liviu

Sie sollten stopAutoManage() in der onPause()-Methode Ihrer Fragment aufrufen:

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

Sie sollten stopAutoManage() in der onPause()-Methode Ihrer Fragment aufrufen:

@Override
public void onPause() {
    super.onPause();

    mGoogleApiClient.stopAutoManage(getActivity());
    mGoogleApiClient.disconnect();
}
32
Adewale Balogun

Um weitere Probleme zu vermeiden

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

Ich hatte ein ähnliches Problem, als ich einen Login-Button in zwei verschiedenen Fragments platzierte, die zu derselben Activity gehörten.

Ich habe dieses Problem gelöst, indem ich jeder automatisch verwalteten GoogleApiClient unterschiedliche IDs zuweist.

Zum Beispiel in Fragment 1 beim Erstellen meines GoogleApiClient-Objekts, dem ich 0 als ID zugewiesen habe:

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

In Fragment 2, während ich mein GoogleApiClient-Objekt erstellte, dem ich 1 als ID zugewiesen habe:

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

Das offizielle Dokument für enableAutoManage sagt folgendes:

Zu jeder Zeit ist nur ein automatisch verwalteter Client pro ID zulässig. Um eine ID wiederzuverwenden, müssen Sie zunächst stopAutoManage (FragmentActivity) auf dem vorherigen Client aufrufen.

In Ihrem Code wird die Version von enableAutoManage ohne Parameter clientId verwendet. Der Standardwert ist 0. Nachstehend werde ich Ihnen erklären, warum Sie mehrere automatisch verwaltete Clients für clientId 0 verwenden. Vor dieser Warnung wird in der obigen Dokumentation gewarnt.

Nachdem Ihr Anmeldungsfragment an eine FragmentActivity angefügt wurde, weist es diese Aktivität an, mit der Verwaltung einer neuen Instanz von GoogleApiClient zu beginnen. Was aber, wenn FragmentActivity bereits eine andere Instanz von GoogleApiClient verwaltet? Dann bekommst du den Fehler.

Es gibt einige mögliche Szenarien, die zu dieser Situation mit mehreren GoogleApiClients-per-FragmentActivity führen können.

  • Die FragmentActivity enthält zusätzlich zum Login ein weiteres Fragment, das einen GoogleApiClient erstellt und auch die FragmentActivity zur Verwaltung auffordert.
  • Die FragmentActivity erstellt selbst einen GoogleApiClient und beginnt mit der Verwaltung, bevor das Login-Fragment an die FragmentActivity angehängt wird.
  • Vielleicht fügen Sie in einer FragmentTransaction das Anmeldefragment hinzu und rufen AddToBackStack auf. Dann tippt der Benutzer zurück, und später wird das Anmeldefragment irgendwie erneut angefügt. In diesem Fall lauten die wichtigsten Aufrufe der Login-Aktivitätsmethode onCreateView -> onDestroyView -> onCreateView:

     fragment lifecycle diagram

Dies ist problematisch, da der zweite Aufruf von Login.onCreateView versucht, FragmentActivity einen zweiten GoogleApiClient verwalten zu lassen.

Wenn ich Sie wäre, würde ich ernsthaft in Betracht ziehen, den GoogleApiClient in der Aktivität zu erstellen, anstatt in Fragmenten. Dann können Sie entweder die Arbeit ausführen, für die der GoogleApiClient in der Aktivität erforderlich ist, oder Sie können dies im Anmelde-Fragment fortsetzen, nachdem Sie den GoogleApiClient aus der Aktivität abgerufen haben.

private GoogleApiClient googleApiClient;

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

@Override
void onDetach() {
    super.onDetach();
    googleApiClient = null;
}
5
vlazzle

Ich schlage vor, dass Sie Ihre mGoogleApiClient in onCreate() statt in onCreateView() initialisieren.

Wie auf @vlazzle hinweist, kann onCreateView() während der Lebensdauer einer einzelnen Activity mehr als einmal aufgerufen werden.

1
pauminku

Sie sollten stopAutoManage() in der onDestroy()-Methode Ihres Fragments wie folgt aufrufen: 

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