没有附加到窗口管理器的 android 视图

我有以下一些例外:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:191)
at android.view.Window$LocalWindowManager.updateViewLayout(Window.java:428)
at android.app.Dialog.onWindowAttributesChanged(Dialog.java:596)
at android.view.Window.setDefaultWindowFormat(Window.java:1013)
at com.android.internal.policy.impl.PhoneWindow.access$700(PhoneWindow.java:86)
at com.android.internal.policy.impl.PhoneWindow$DecorView.drawableChanged(PhoneWindow.java:1951)
at com.android.internal.policy.impl.PhoneWindow$DecorView.fitSystemWindows(PhoneWindow.java:1889)
at android.view.ViewRoot.performTraversals(ViewRoot.java:727)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4338)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)

I have googled it and see that it has something to do with popups and turning the screen, but there is no reference to my code.

问题是:

  1. 有没有办法查出来 这个问题到底是什么时候 发生了什么?
  2. 除了转动屏幕,是否还有其他事件或动作触发这个错误?
  3. 我该怎么阻止这种情况发生?
112543 次浏览

问题1) :

考虑到错误消息似乎没有说明是哪一行代码造成了问题,您可以通过使用断点来跟踪它。当程序到达特定的代码行时,断点会暂停程序的执行。通过向关键位置添加断点,可以确定导致崩溃的代码行。例如,如果您的程序在 setContentView ()行崩溃,您可以在那里放置一个断点。当程序运行时,它将在运行该行之前暂停。如果然后恢复导致程序在到达下一个断点之前崩溃,那么您就知道杀死程序的那一行是在两个断点之间。

如果您正在使用 Eclipse,添加断点是很容易的。在代码左侧的空白处右键单击并选择“切换断点”。然后,您需要在调试模式下运行应用程序,这个按钮看起来像普通运行按钮旁边的一个绿色昆虫。当程序遇到断点时,Eclipse 将切换到调试透视图并向您显示它正在等待的行。要重新启动程序,请查找“ Resume”按钮,它看起来像一个正常的“ Play”,但在三角形的左侧有一个垂直条。

You can also fill your application with Log.d("My application", "Some information here that tells you where the log line is"), which then posts messages in Eclipse's LogCat window. If you can't find that window, open it up with Window -> Show View -> Other... -> Android -> LogCat.

希望能帮上忙!

I added the following to the manifest for that activity

android:configChanges="keyboardHidden|orientation|screenLayout"

我遇到了这个问题,在屏幕方向改变时,活动在 AsyncTask 之前完成,进度对话框也完成了。我似乎通过将对话框设置为 null onPause(),然后在解除之前在 AsyncTask 中检查它来解决这个问题。

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


if ((mDialog != null) && mDialog.isShowing())
mDialog.dismiss();
mDialog = null;
}

在我的异步任务里:

protected void onPreExecute() {
mDialog = ProgressDialog.show(mContext, "", "Saving changes...",
true);
}


protected void onPostExecute(Object result) {
if ((mDialog != null) && mDialog.isShowing()) {
mDialog.dismiss();
}
}

当您在清单中声明活动时,您需要 android: configChanges = “  方向”

example:

<activity android:theme="@android:style/Theme.Light.NoTitleBar" android:configChanges="orientation"  android:label="traducción" android:name=".PantallaTraductorAppActivity"></activity>

Or Simply you Can add

protected void onPreExecute() {
mDialog = ProgressDialog.show(mContext, "", "Saving changes...", true, false);
}

这将使 ProgressDialog不能取消

根据 windowManager 的代码(链接 这里) ,当你试图更新的视图(可能属于一个对话框,但不是必需的)不再附加到窗口的真正根目录时,就会发生这种情况。

正如其他人建议的那样,在对对话框执行特殊操作之前,应该检查活动的状态。

下面是相关的代码,这就是问题的原因(从 Android 源代码复制而来) :

public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}


final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;


view.setLayoutParams(wparams);


synchronized (this) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots[index];
mParams[index] = wparams;
root.setLayoutParams(wparams, false);
}
}


private int findViewLocked(View view, boolean required) {
synchronized (this) {
final int count = mViews != null ? mViews.length : 0;
for (int i=0; i<count; i++) {
if (mViews[i] == view) {
return i;
}
}
if (required) {
throw new IllegalArgumentException(
"View not attached to window manager");
}
return -1;
}
}

我的问题解决了,解锁屏幕旋转在我的机器人应用程序,这给我造成了一个问题,现在工作完美

另一种选择是,在通过重写对话框上的 onAttachedToWindow ()将对话框附加到窗口之前,不要启动异步任务,这样总是不允许执行该任务。

如果你有一个 Activity对象,你可以使用 isDestroyed()方法:

Activity activity;


// ...


if (!activity.isDestroyed()) {
// ...
}

如果您有一个非匿名的 AsyncTask子类,可以在不同的地方使用它,那么这样做很好。

我正在使用一个自定义静态类,它可以显示和隐藏对话框。 这个类被其他活动所使用,而不仅仅是一个活动。 现在你所描述的问题也出现在我面前,我留下来过夜寻找解决办法。

最后我给你们展示一下解决方案!

如果你想显示或者关闭一个对话框,但是你不知道是哪个活动启动了这个对话框来触摸它,那么下面的代码就是为你准备的。.

 static class CustomDialog{


public static void initDialog(){
...
//init code
...
}


public static void showDialog(){
...
//init code for show dialog
...
}


/****This is your Dismiss dialog code :D*******/
public static void dismissProgressDialog(Context context) {
//Can't touch other View of other Activiy..
//http://stackoverflow.com/questions/23458162/dismiss-progress-dialog-in-another-activity-android
if ( (progressdialog != null) && progressdialog.isShowing()) {


//is it the same context from the caller ?
Log.w("ProgressDIalog dismiss", "the dialog is from"+progressdialog.getContext());


Class caller_context= context.getClass();
Activity call_Act = (Activity)context;
Class progress_context= progressdialog.getContext().getClass();


Boolean is_act= ( (progressdialog.getContext()) instanceof  Activity )?true:false;
Boolean is_ctw= ( (progressdialog.getContext()) instanceof  ContextThemeWrapper )?true:false;


if (is_ctw) {
ContextThemeWrapper cthw=(ContextThemeWrapper) progressdialog.getContext();
Boolean is_same_acivity_with_Caller= ((Activity)(cthw).getBaseContext() ==  call_Act )?true:false;


if (is_same_acivity_with_Caller){
progressdialog.dismiss();
progressdialog = null;
}
else {
Log.e("ProgressDIalog dismiss", "the dialog is NOT from the same context! Can't touch.."+((Activity)(cthw).getBaseContext()).getClass());
progressdialog = null;
}
}




}
}


}

为什么不试试接球,像这样:

protected void onPostExecute(Object result) {
try {
if ((mDialog != null) && mDialog.isShowing()) {
mDialog.dismiss();
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
}
}

在与这个问题斗争之后,我最终得到了这个变通方法:

/**
* Dismiss {@link ProgressDialog} with check for nullability and SDK version
*
* @param dialog instance of {@link ProgressDialog} to dismiss
*/
public void dismissProgressDialog(ProgressDialog dialog) {
if (dialog != null && dialog.isShowing()) {


//get the Context object that was used to great the dialog
Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();


// if the Context used here was an activity AND it hasn't been finished or destroyed
// then dismiss it
if (context instanceof Activity) {


// Api >=17
if (!((Activity) context).isFinishing() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (!((Activity) context).isDestroyed()) {
dismissWithExceptionHandling(dialog);
}
} else {
// Api < 17. Unfortunately cannot check for isDestroyed()
dismissWithExceptionHandling(dialog);
}
}
} else
// if the Context used wasn't an Activity, then dismiss it too
dismissWithExceptionHandling(dialog);
}
dialog = null;
}
}


/**
* Dismiss {@link ProgressDialog} with try catch
*
* @param dialog instance of {@link ProgressDialog} to dismiss
*/
public void dismissWithExceptionHandling(ProgressDialog dialog) {
try {
dialog.dismiss();
} catch (final IllegalArgumentException e) {
// Do nothing.
} catch (final Exception e) {
// Do nothing.
} finally {
dialog = null;
}
}

有时候,如果没有更好的解决方案,良好的异常处理会工作得很好。

Above solution didn't work for me. So what I did is take ProgressDialog as globally and then add this to my activity

@Override
protected void onDestroy() {
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
super.onDestroy();
}

so that in case if activity is destroyed then the ProgressDialog will also be destroy.