Development Tip

조각 오류 :“ID가 0 인 GoogleApiClient를 이미 관리하고 있습니다.”

yourdevel 2020. 12. 9. 21:52
반응형

조각 오류 :“ID가 0 인 GoogleApiClient를 이미 관리하고 있습니다.”


처음에는 모든 것이 올바르게 작동하며 두 번째로 실행하면 다음 오류가 표시됩니다.

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)

내 코드는 다음과 같습니다.

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;
        }
    }
}

내가 이해할 수 있듯이 문제는 다음 줄에 있습니다.

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

ID를 명시 적으로 전달하려고했습니다 0.

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

하지만 여전히 작동하지 않았습니다.

내가 무엇을 놓치고 있습니까?


다음 stopAutoManage()onPause()메소드를 호출해야 합니다 Fragment.

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

당신은 호출해야 stopAutoManage()onPause()당신의 방법 Fragment같은 :

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

    mGoogleApiClient.stopAutoManage(getActivity());
    mGoogleApiClient.disconnect();
}

추가 문제를 방지하려면

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

Fragment동일한에 속하는 두 개의 다른에 로그인 버튼을 배치했을 때 비슷한 문제가 발생했습니다 Activity.

자동으로 관리되는 각 .NET에 다른 ID를 할당하여이 문제를 해결했습니다 GoogleApiClient.

예를 들어 Fragment1에서 GoogleApiClient개체 를 만드는 동안 0 을 ID로 할당했습니다 .

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

Fragment2 에서는 GoogleApiClient개체 를 만드는 동안 ID로 1할당했습니다 .

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

enableAutoManage공식 문서 는 다음과 같이 말합니다.

주어진 시간에 ID 당 하나의 자동 관리 클라이언트 만 허용됩니다. ID를 재사용하려면 먼저 이전 클라이언트에서 stopAutoManage (FragmentActivity)를 호출해야합니다.

귀하의 코드는 clientId 매개 변수없이 enableAutoManage 버전을 사용하고 있으므로 기본값은 0입니다. 아래에서는 위 문서에서 경고하는 clientId 0에 대해 여러 자동 관리 클라이언트가있는 이유를 설명합니다.

로그인 조각이 FragmentActivity에 연결되면 해당 활동에 GoogleApiClient의 새 인스턴스 관리를 시작하도록 지시합니다. 그러나 FragmentActivity가 이미 GoogleApiClient의 다른 인스턴스를 관리하고 있다면 어떻게 될까요? 그때 오류가 발생합니다.

이 여러 GoogleApiClients-per-FragmentActivity 상황으로 이어질 수있는 몇 가지 가능한 시나리오가 있습니다.

  • FragmentActivity에는 GoogleApiClient를 생성하고 FragmentActivity에이를 관리하도록 요청하는 Login 외에 또 다른 Fragment가 있습니다.
  • FragmentActivity 자체는 GoogleApiClient를 만들고 Login Fragment가 FragmentActivity에 연결되기 전에 관리를 시작합니다.
  • FragmentTransaction에 로그인 조각을 추가하고 addToBackStack을 호출 할 수 있습니다. 그런 다음 사용자는 다시 탭한 다음 나중에 로그인 조각이 다시 연결됩니다. 이 경우 중요한 로그인 활동 메서드 호출은 다음과 같이 onCreateView-> onDestroyView-> onCreateView입니다.

    조각 수명주기 다이어그램

이는 Login.onCreateView에 대한 두 번째 호출이 FragmentActivity가 두 번째 GoogleApiClient를 관리하도록하기 때문에 문제가됩니다.

내가 당신이라면 조각 대신 활동에서 GoogleApiClient를 만드는 것을 진지하게 고려할 것입니다. 그런 다음 활동에서 GoogleApiClient가 필요한 작업을 수행하거나 다음과 같이 활동에서 GoogleApiClient를 가져온 후 로그인 조각에서 계속 수행 할 수 있습니다.

private GoogleApiClient googleApiClient;

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

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

대신 mGoogleApiClientin 을 초기화하는 것이 좋습니다 .onCreate()onCreateView()

As pointed out by @vlazzle, onCreateView() can be called more than once during a single Activity's lifespan.


You should call stopAutoManage() in the onDestroy() method of your fragment like so:

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

The checked answer will not work if you use Login fragment in an Activity multiple times, because adding the fragment sequentially in a short time will lead the same crash. Android sometimes mixes lifecycle of Fragments added in an Activity.

So I advice you to do mGoogleApiClient staff in a separate abstract Activity, and make all activities adding Login fragment extend this Activity.

I have managed to get rid of this crash by creating this abstract Activity below, just copy paste it to your project:

abstract class LoginableActivity : BaseActivity() {

    lateinit var googleApiClient: GoogleApiClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.fcm))
            .requestEmail()
            .build()

        googleApiClient = GoogleApiClient.Builder(this)
            .enableAutoManage(this, 1, null)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build()
    }

    override fun onStart() {
        super.onStart()
        if (!googleApiClient.isConnected && !googleApiClient.isConnecting) {
            googleApiClient.connect()
        }
    }

    override fun onStop() {
        super.onStop()
        if (googleApiClient.isConnected) {
            googleApiClient.stopAutoManage(this)
            googleApiClient.disconnect()
        }
    }
}

당신이 이동 후 googleApiClientLoginableActivity액세스 할 수 있습니다 googleApiClient에서 Login이 같은 조각 (의 자바와 함께 할 수 있습니다)

final Activity = getActivity();
if (activity instanceof LoginableActivity) {
    final GoogleApiClient googleApiClient = ((LoginableActivity) activity).googleApiClient;
}

mGoogleApiClient당신의 활동에서 사용하십시오 . GoogleApiClient활동에 대해 선언 한 경우 조각에서 다시 선언 할 수 없습니다. 대신 해당 활동의 변수를 조각에서 재사용하십시오.

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

YouractivityClass조각의 활동으로 대체 하고 활동의 mGoogleApiClient필드를 공개로 설정하십시오.

참고 URL : https://stackoverflow.com/questions/36105118/error-in-fragment-already-managing-a-googleapiclient-with-id-0

반응형