Java 片段中的错误:“已经在管理 ID 为 0 的 GoogleApiClient”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36105118/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Error in Fragment: "Already managing a GoogleApiClient with id 0"
提问by diaconu liviu
Everything works right the first time, if you launch a second time you see this error:
第一次一切正常,如果你第二次启动,你会看到这个错误:
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.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)
Here's my 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;
}
}
}
As I understand it, the problem is in these lines:
据我了解,问题出在以下几行:
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.enableAutoManage(getActivity()/* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
I tried explicitly passing an id of 0
:
我尝试明确传递一个 id 0
:
.enableAutoManage(getActivity() /* FragmentActivity */, 0, this /* OnConnectionFailedListener */)
but that still didn't work.
但这仍然不起作用。
What am I missing?
我错过了什么?
采纳答案by Sudara
You should call stopAutoManage()
in the onPause()
method of your Fragment
:
你应该调用你stopAutoManage()
的onPause()
方法Fragment
:
@Override
public void onPause() {
super.onPause();
mGoogleClient.stopAutoManage(getActivity());
mGoogleClient.disconnect();
}
回答by Adewale Balogun
You should call stopAutoManage()
in the onPause()
method of your Fragment
like so:
你应该像这样调用你stopAutoManage()
的onPause()
方法Fragment
:
@Override
public void onPause() {
super.onPause();
mGoogleApiClient.stopAutoManage(getActivity());
mGoogleApiClient.disconnect();
}
回答by Bamz3r
Try to use your mGoogleApiClient
from your activity.
if you have declared GoogleApiClient
on your activity, then you can't re-declared on your fragment. instead, re-use variable in that activity from fragment
尝试使用您mGoogleApiClient
的活动。如果你已经GoogleApiClient
在你的活动中声明,那么你不能在你的片段上重新声明。相反,从片段中重用该活动中的变量
mGoogleApiClientInFragment = ((Youractivityclass)getActivity()).mGoogleApiClient;
replace YouractivityClass
with your activity of your fragment, and make sure set your mGoogleApiClient
field in your activity to public
替换YouractivityClass
为您片段的活动,并确保将您mGoogleApiClient
的活动中的字段设置为公开
回答by nadafafif
To avoid further issue
为避免进一步的问题
@Override
public void onStop() {
super.onStop();
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
mGoogleApiClient.stopAutoManage((Activity) context);
mGoogleApiClient.disconnect();
}
}
回答by vlazzle
The official doc for enableAutoManagesays this:
At any given time, only one auto-managed client is allowed per id. To reuse an id you must first call stopAutoManage(FragmentActivity) on the previous client.
在任何给定时间,每个 ID 只允许一个自动管理的客户端。要重用 id,您必须首先在前一个客户端上调用 stopAutoManage(FragmentActivity)。
Your code is using the version of enableAutoManage without a clientId parameter, so it's defaulting to 0. Below I explain why you will have multiple auto-managed clients for clientId 0, which is what the above documentation is warning against.
您的代码正在使用没有 clientId 参数的 enableAutoManage 版本,因此默认为 0。下面我将解释为什么您将有多个自动管理的 clientId 0 客户端,这是上述文档警告的内容。
After your Login Fragment is attached to a FragmentActivity, it tells that activity to start managing a new instance of GoogleApiClient. But what if the FragmentActivity is already managing another instance of GoogleApiClient? That's when you get the error.
在您的登录 Fragment 附加到 FragmentActivity 后,它会告诉该活动开始管理 GoogleApiClient 的新实例。但是如果 FragmentActivity 已经在管理 GoogleApiClient 的另一个实例呢?那是你得到错误的时候。
There are a few possible scenarios that can lead up to this multiple-GoogleApiClients-per-FragmentActivity situation.
有几种可能的情况会导致这种多 GoogleApiClients-per-FragmentActivity 情况。
- The FragmentActivity has another Fragment in addition to Login that creates a GoogleApiClient and also asks the FragmentActivity to manage it.
- The FragmentActivity itself creates a GoogleApiClient and starts managing it before Login Fragment is attached to the FragmentActivity.
Maybe you add Login Fragment in a FragmentTransaction and you call addToBackStack. Then the user taps back, then later somehow the Login Fragment is re-attached. In this case, the important Login Activity method calls are onCreateView -> onDestroyView -> onCreateView as shown here:
- 除了 Login 之外,FragmentActivity 还有另一个 Fragment,它创建一个 GoogleApiClient 并要求 FragmentActivity 管理它。
- FragmentActivity 本身会创建一个 GoogleApiClient 并在登录 Fragment 附加到 FragmentActivity 之前开始管理它。
也许您在 FragmentTransaction 中添加 Login Fragment 并调用 addToBackStack。然后用户回击,然后以某种方式重新附加登录片段。在这种情况下,重要的登录活动方法调用是 onCreateView -> onDestroyView -> onCreateView,如下所示:
This is problematic because the second call to Login.onCreateView tries to have FragmentActivity manage a second GoogleApiClient.
这是有问题的,因为对 Login.onCreateView 的第二次调用试图让 FragmentActivity 管理第二个 GoogleApiClient。
If I were you, I'd seriously consider creating the GoogleApiClient in the activity instead of in any fragments. Then you could either do the work that requires GoogleApiClient in the Activity, or continue to do it in Login Fragment after getting the GoogleApiClient from the Activity like so:
如果我是你,我会认真考虑在活动中而不是在任何片段中创建 GoogleApiClient。然后,您可以在 Activity 中执行需要 GoogleApiClient 的工作,也可以在从 Activity 获取 GoogleApiClient 后继续在 Login Fragment 中执行此操作,如下所示:
private GoogleApiClient googleApiClient;
@Override
void onAttach(Activity activity) {
super.onAttach(activity);
googleApiClient = activity.getGoogleApiClient();
}
@Override
void onDetach() {
super.onDetach();
googleApiClient = null;
}
回答by Arshak
I faced a similar issue when I placed a login button in two different Fragment
s belonging to the same Activity
.
当我将登录按钮放在Fragment
属于同一Activity
.
I solved this issue by assigning different ids to each automatically-managed GoogleApiClient
.
我通过为每个自动管理的GoogleApiClient
.
For example, in Fragment
1, while creating my GoogleApiClient
object I assigned 0as the id:
例如,在Fragment
1 中,在创建GoogleApiClient
对象时,我将 0 指定为 id:
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.enableAutoManage(getActivity(), 0, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
In Fragment
2, while creating my GoogleApiClient
object I assigned 1as the id:
在Fragment
2 中,在创建GoogleApiClient
对象时,我指定了 1作为 id:
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.enableAutoManage(getActivity(), 1, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
回答by pauminku
I suggest that you initialize your mGoogleApiClient
in onCreate()
instead of in onCreateView()
.
我建议你初始化你的mGoogleApiClient
inonCreate()
而不是 in onCreateView()
。
As pointed out by @vlazzle, onCreateView()
can be called more than once during a single Activity
's lifespan.
回答by Egemen Hamut?u
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.
如果您Login
在 Activity 中多次使用片段,则检查的答案将不起作用,因为在短时间内顺序添加片段会导致相同的崩溃。Android 有时会混合添加到 Activity 中的 Fragment 的生命周期。
So I advice you to do mGoogleApiClient
staff in a separate abstract Activity, and make all activities adding Login
fragment extend this Activity.
所以我建议你mGoogleApiClient
在一个单独的抽象Activity中做员工,并让所有添加Login
片段的Activity都扩展到这个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()
}
}
}
After you moved googleApiClient
to LoginableActivity
you can access googleApiClient
from Login
fragment like this (let's do it with Java):
移动googleApiClient
到之后,LoginableActivity
您可以像这样googleApiClient
从Login
片段访问(让我们用 Java 来实现):
final Activity = getActivity();
if (activity instanceof LoginableActivity) {
final GoogleApiClient googleApiClient = ((LoginableActivity) activity).googleApiClient;
}
回答by Kishan Thakkar
You should call stopAutoManage()
in the onDestroy()
method of your fragment like so:
您应该像这样调用片段stopAutoManage()
的onDestroy()
方法:
@Override
public void onDestroy() {
super.onDestroy();
mGoogleApiClient.stopAutoManage(getActivity());
mGoogleApiClient.disconnect();
}
回答by Oscar Emilio Perez Martinez
This works for me to avoid crashing issues using kotlin
这对我有用,可以避免使用 kotlin 出现崩溃问题
private lateinit var googleApiClient: GoogleApiClient
And then just verify if the variable is already initialized
然后只需验证变量是否已经初始化
if(!::googleApiClient.isInitialized) {
googleApiClient = GoogleApiClient.Builder(context)
.enableAutoManage(activity, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, options)
.build()
}