Android 视图未附加到窗口管理器崩溃

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22924825/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:34:04  来源:igfitidea点击:

View not attached to window manager crash

android

提问by Howli

I am using ACRA to report app crashes. I was getting a View not attached to window managererror message and thought I had fixed it by wrapping the pDialog.dismiss();in an if statement:

我正在使用 ACRA 报告应用程序崩溃。我收到一条View not attached to window manager错误消息,并认为我已经通过将pDialog.dismiss();if 语句包装起来修复了它:

if (pDialog!=null) 
{
    if (pDialog.isShowing()) 
    {
        pDialog.dismiss();   
    }
}

It has reduced the amount of View not attached to window managercrashes I recieve, but I am still getting some and I am not sure how to solve it.

它减少了View not attached to window manager我收到的崩溃次数,但我仍然收到一些崩溃,我不知道如何解决它。

Error message:

错误信息:

java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:425)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:327)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:83)
at android.app.Dialog.dismissDialog(Dialog.java:330)
at android.app.Dialog.dismiss(Dialog.java:312)
at com.package.class$LoadAllProducts.onPostExecute(class.java:624)
at com.package.class$LoadAllProducts.onPostExecute(class.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access0(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5419)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
at dalvik.system.NativeStart.main(Native Method)

Code snippet:

代码片段:

class LoadAllProducts extends AsyncTask<String, String, String> 
{

    /**
     * Before starting background thread Show Progress Dialog
     * */
    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();
        pDialog = new ProgressDialog(CLASS.this);
        pDialog.setMessage("Loading. Please wait...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(false);
        pDialog.show();
    }

    /**
     * getting All products from url
     * */
    protected String doInBackground(String... args) 
    {
        // Building Parameters
        doMoreStuff("internet");
        return null;
    }


    /**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) 
    {
         // dismiss the dialog after getting all products
         if (pDialog!=null) 
         {
                if (pDialog.isShowing()) 
                {
                    pDialog.dismiss();   //This is line 624!    
                }
         }
         something(note);
    }
}

Manifest:

显现:

    <activity
        android:name="pagename.CLASS" 
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout"            
        android:label="@string/name" >
    </activity>

What am I missing to stop this crash from happening?

我错过了什么来阻止这次崩溃的发生?

回答by erakitin

How to reproduce the bug:

如何重现错误:

  1. Enable this option on your device: Settings -> Developer Options -> Don't keep Activities.
  2. Press Home button while the AsyncTaskis executing and the ProgressDialogis showing.
  1. 在您的设备上启用此选项:Settings -> Developer Options -> Don't keep Activities
  2. AsyncTask执行和ProgressDialog显示时按主页按钮。

The Android OS will destroy an activity as soon as it is hidden. When onPostExecuteis called the Activitywill be in "finishing"state and the ProgressDialogwill be not attached to Activity.

Android 操作系统会在活动隐藏后立即销毁活动。当onPostExecute被调用时,Activity将处于“完成”状态并且ProgressDialog将不附加到Activity.

How to fix it:

如何修复:

  1. Check for the activity state in your onPostExecutemethod.
  2. Dismiss the ProgressDialogin onDestroymethod. Otherwise, android.view.WindowLeakedexception will be thrown. This exception usually comes from dialogs that are still active when the activity is finishing.
  1. 检查方法中的活动状态onPostExecute
  2. 关闭ProgressDialoginonDestroy方法。否则android.view.WindowLeaked会抛出异常。此异常通常来自活动完成时仍处于活动状态的对话框。

Try this fixed code:

试试这个固定的代码:

public class YourActivity extends Activity {

    private void showProgressDialog() {
        if (pDialog == null) {
            pDialog = new ProgressDialog(StartActivity.this);
            pDialog.setMessage("Loading. Please wait...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(false);
        }
        pDialog.show();
    }

    private void dismissProgressDialog() {
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }

    @Override
    protected void onDestroy() {
        dismissProgressDialog();
        super.onDestroy();
    }

    class LoadAllProducts extends AsyncTask<String, String, String> {

        // Before starting background thread Show Progress Dialog
        @Override
        protected void onPreExecute() {
            showProgressDialog();
        }

        //getting All products from url
        protected String doInBackground(String... args) {
            doMoreStuff("internet");
            return null;
        }

        // After completing background task Dismiss the progress dialog
        protected void onPostExecute(String file_url) {
            if (YourActivity.this.isDestroyed()) { // or call isFinishing() if min sdk version < 17
                return;
            }
            dismissProgressDialog();
            something(note);
        }
    }
}

回答by Libin

Issue could be that the Activityhave been finishedor in progress of finishing.

问题可能是Activityhas beenfinished或 in progress of finishing

Add a check isFinishing, and dismiss dialog only when this is false

添加一个检查isFinishing,并仅在此情况下关闭对话框false

if (!YourActivity.this.isFinishing() && pDialog != null) {
    pDialog.dismiss();
}

isFinishing :Check to see whether this activity is in the process of finishing,either because you called finishon it or someone else has requested that it finished.

isFinishing :检查此活动是否正在完成,因为您调用finish了它或其他人要求它完成。

回答by Teng-pao Yu

For Dialogcreated in a Fragment, I use the following code:

对于Dialog在 a 中创建Fragment,我使用以下代码:

ProgressDialog myDialog = new ProgressDialog(getActivity());
myDialog.setOwnerActivity(getActivity());
...
Activity activity = myDialog.getOwnerActivity();
if( activity!=null && !activity.isFinishing()) {
    myDialog.dismiss();
}

I use this pattern to deal with the case when a Fragmentmay be detached from the Activity.

我使用这种模式来处理 aFragment可能与Activity.

回答by Kailas

See how the Code is working here:

在这里查看代码是如何工作的:

After calling the Async task, the async task runs in the background. that is desirable. Now, this Async task has a progress dialog which is attached to the Activity, if you ask how to see the code:

调用异步任务后,异步任务在后台运行。这是可取的。现在,如果您询问如何查看代码,此异步任务有一个附加到 Activity 的进度对话框:

pDialog = new ProgressDialog(CLASS.this);

You are passing the Class.thisas context to the argument. So the Progress dialog is still attached to the activity.

您将Class.thisas 上下文传递给参数。所以进度对话框仍然附加到活动。

Now consider the scenario: If we try to finish the activity using the finish() method, while the async task is in progress, is the point where you are trying to access the Resource attached to the activity ie the progress barwhen the activity is no more there.

现在考虑以下场景:如果我们尝试使用 finish() 方法完成活动,而异步任务正在进行中,则是您尝试访问附加到活动的资源的点,即progress bar活动不再时那里。

Hence you get:

因此你得到:

java.lang.IllegalArgumentException: View not attached to the window manager

Solution to this:

对此的解决方案:

1) Make sure that the Dialog box is dismissed or canceled before the activity finishes.

1) 确保在活动完成之前关闭或取消对话框。

2) Finish the activity, only after the dialog box is dismissed, that is the async task is over.

2)完成activity,只有在对话框被关闭后,即异步任务结束。

回答by Kapé

Based on @erakitin answer, but also compatible for Android versions < API level 17. Sadly Activity.isDestroyed() is only supported since API level 17, so if you're targeting an older API level just like me, you'll have to check it yourself. Haven't got the View not attached to window managerexception after that.

基于@erakitin 答案,但也兼容 <API 级别 17 的 Android 版本。遗憾的是Activity.isDestroyed()仅自 API 级别 17 起才受支持,因此如果您像我一样针对较旧的 API 级别,则必须自己检查一下。View not attached to window manager之后就没有异常了。

Example code

示例代码

public class MainActivity extends Activity {
    private TestAsyncTask mAsyncTask;
    private ProgressDialog mProgressDialog;
    private boolean mIsDestroyed;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (condition) {
            mAsyncTask = new TestAsyncTask();
            mAsyncTask.execute();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mAsyncTask != null && mAsyncTask.getStatus() != AsyncTask.Status.FINISHED) {
            Toast.makeText(this, "Still loading", Toast.LENGTH_LONG).show();
            return;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIsDestroyed = true;

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

    public class TestAsyncTask extends AsyncTask<Void, Void, AsyncResult> {    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgressDialog = ProgressDialog.show(MainActivity.this, "Please wait", "doing stuff..");
        }

        @Override
        protected AsyncResult doInBackground(Void... arg0) {
            // Do long running background stuff
            return null;
        }

        @Override
        protected void onPostExecute(AsyncResult result) {
            // Use MainActivity.this.isDestroyed() when targeting API level 17 or higher
            if (mIsDestroyed)// Activity not there anymore
                return;

            mProgressDialog.dismiss();
            // Handle rest onPostExecute
        }
    }
}

回答by Meghna

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

    if(pDialog != null)
        pDialog .dismiss();
    pDialog = null;
}

refer this.

参考这个

回答by surya

Override onDestroy of the Activity and Dismiss your Dialog & make it null

覆盖活动的 onDestroy 并关闭您的对话框并使其为空

protected void onDestroy ()
    {
        if(mProgressDialog != null)
            if(mProgressDialog.isShowing())
                mProgressDialog.dismiss();
        mProgressDialog= null;
    }

回答by Pratapi Hemant Patel

Override onConfigurationChanged and dismiss progress dialog. If progress dialog is created in portrait and dismisses in landscape then it will throw View not attached to window manager error.

覆盖 onConfigurationChanged 并关闭进度对话框。如果在纵向创建进度对话框并在横向关闭,那么它将抛出 View not attach to window manager 错误。

Also stop the progress bar and stop the async task in onPause(), onBackPressed and onDestroy method.

同时停止进度条并停止 onPause()、onBackPressed 和 onDestroy 方法中的异步任务。

if(asyncTaskObj !=null && asyncTaskObj.getStatus().equals(AsyncTask.Status.RUNNING)){

    asyncTaskObj.cancel(true);

}

回答by aolphn

Firstly,the crash reason is decorView's index is -1,we can knew it from Android source code ,there is code snippet:

首先,崩溃的原因是decorView的索引为-1,我们可以从Android源代码中知道它,有代码片段:

class:android.view.WindowManagerGlobal

file:WindowManagerGlobal.java

类:android.view.WindowManagerGlobal

文件:WindowManagerGlobal.java

private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
//here, view is decorView,comment by OF
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

so we get follow resolution,just judge decorView's index,if it more than 0 then continue or just return and give up dismiss,code as follow:

所以我们得到follow resolution,只判断decorView的index,如果大于0则继续或者直接返回放弃dismiss,代码如下:

try {
            Class<?> windowMgrGloable = Class.forName("android.view.WindowManagerGlobal");
            try {
                Method mtdGetIntance = windowMgrGloable.getDeclaredMethod("getInstance");
                mtdGetIntance.setAccessible(true);
                try {
                    Object windownGlobal = mtdGetIntance.invoke(null,null);
                    try {
                        Field mViewField = windowMgrGloable.getDeclaredField("mViews");
                        mViewField.setAccessible(true);
                        ArrayList<View> mViews = (ArrayList<View>) mViewField.get(windownGlobal);
                        int decorViewIndex = mViews.indexOf(pd.getWindow().getDecorView());
                        Log.i(TAG,"check index:"+decorViewIndex);
                        if (decorViewIndex < 0) {
                            return;
                        }
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (pd.isShowing()) {
            pd.dismiss();
        }

回答by thundertrick

Overide the dismiss()method like this:

dismiss()像这样覆盖方法:

@Override
public void dismiss() {
    Window window = getWindow();
    if (window == null) {
        return;
    }
    View decor = window.getDecorView();
    if (decor != null && decor.getParent() != null) {
        super.dismiss();
    }
}

To reproduce the issue, just finish activity before dismiss dialog.

要重现该问题,只需在关闭对话框之前完成活动即可。