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
View not attached to window manager crash
提问by Howli
I am using ACRA to report app crashes. I was getting a View not attached to window manager
error 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 manager
crashes 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:
如何重现错误:
- Enable this option on your device:
Settings -> Developer Options -> Don't keep Activities
. - Press Home button while the
AsyncTask
is executing and theProgressDialog
is showing.
- 在您的设备上启用此选项:
Settings -> Developer Options -> Don't keep Activities
。 - 在
AsyncTask
执行和ProgressDialog
显示时按主页按钮。
The Android OS will destroy an activity as soon as it is hidden. When onPostExecute
is called the Activity
will be in "finishing"state and the ProgressDialog
will be not attached to Activity
.
Android 操作系统会在活动隐藏后立即销毁活动。当onPostExecute
被调用时,Activity
将处于“完成”状态并且ProgressDialog
将不附加到Activity
.
How to fix it:
如何修复:
- Check for the activity state in your
onPostExecute
method. - Dismiss the
ProgressDialog
inonDestroy
method. Otherwise,android.view.WindowLeaked
exception will be thrown. This exception usually comes from dialogs that are still active when the activity is finishing.
- 检查方法中的活动状态
onPostExecute
。 - 关闭
ProgressDialog
inonDestroy
方法。否则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 Activity
have been finished
or in progress of finishing
.
问题可能是Activity
has 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 finish
on it or someone else has requested that it finished.
isFinishing :检查此活动是否正在完成,因为您调用finish
了它或其他人要求它完成。
回答by Teng-pao Yu
For Dialog
created 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 Fragment
may 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.this
as context to the argument. So the Progress dialog is still attached to the activity.
您将Class.this
as 上下文传递给参数。所以进度对话框仍然附加到活动。
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 bar
when 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 manager
exception 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
回答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.
要重现该问题,只需在关闭对话框之前完成活动即可。