창 관리자 충돌에 첨부되지 않은보기


189

ACRA를 사용하여 앱 충돌을보고합니다. 내가지고 있다고 View not attached to window manager오류 메시지와 나는 포장하여 고정했다고 생각 pDialog.dismiss();if 문에서 :

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

View not attached to window manager수신 되는 충돌 의 양이 줄어들 었지만 여전히 일부 문제가 발생하여 해결 방법을 잘 모르겠습니다.

에러 메시지:

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.access$600(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)

코드 스 니펫 :

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

명백한:

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

이 충돌이 발생하지 않도록 무엇을 놓치고 있습니까?


1
이것을 알아 낸 적이 있습니까? 같은 문제가 있습니다. 알아낼 수 없습니다.
tomjung

불행하게도. 나는 현상금을 조금 시작할 수 있습니다. 문제를 해결하는 다른 스레드 중 일부를 확인해야합니다.
Howli

귀하는 AsyncTask내부에 선언 Activity이나 Fragment?
에라 키틴

"doMoreStuff ()"및 "something ()"함수를 게시하십시오.
berserk

이 문제는 메인 스레드의 작업이 너무 많아서 처리기를 사용하여 진행률 대화 상자를 표시하고 isShowing 자체가 대화 상자가 진행 중인지 여부를 확인하기 때문에 (pDialog! = null)이 행이 필요하지 않은 경우 시도하십시오.
Madhu

답변:


446

버그를 재현하는 방법 :

  1. 장치에서이 옵션을 활성화하십시오 : Settings -> Developer Options -> Don't keep Activities.
  2. AsyncTask가 실행 중이고 ProgressDialog가 표시된 상태에서 홈 버튼을 누릅니다 .

Android OS는 숨겨 지 자마자 활동을 파괴합니다. 때 onPostExecute호출되는 Activity될 것 "마무리" 상태와 ProgressDialog부착되지는 않습니다 Activity.

해결 방법 :

  1. onPostExecute분석법 에서 활동 상태를 확인하십시오 .
  2. ProgressDialogin onDestroy메소드를 해제하십시오 . 그렇지 않으면 android.view.WindowLeaked예외가 발생합니다. 이 예외는 일반적으로 활동이 완료 될 때 여전히 활성 인 대화 상자에서 발생합니다.

이 고정 코드를 사용해보십시오 :

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

9
좋은 답변입니다! BTW, isShowing ()은 isShowing () == false 인 경우 dismiss ()가 아무것도하지 않기 때문에 불필요합니다. 소스 코드
Peter Zhao

3
이 특정 목적으로 모든 API에서 isDestroyed()over isFinishing()를 사용하면 어떤 이점 이 있습니까?
Alexander Abakumov

@ AlexanderAbakumov : 활동이 시스템에 의해 파괴 된 경우 isFinishing()가 아니라는 것을 이해 하지 못 true하므로 활동 문서를 참조하십시오 .
Markus Penguin

1
@MarkusPenguin : 그렇습니다. 그러나이 경우 저자의 의견에서 조언을 사용하려고 시도하면 // or call isFinishing() if min sdk version < 17동일한 예외가 발생합니다. 따라서 API <17에서 실행되는 앱의 경우이 답변과 다른 솔루션이 필요합니다.
Alexander Abakumov

: @AlexanderAbakumov 저도 같은 문제가있어,하지만 isFinishing 나를 위해 작동하지 않습니다, 그것은 AsyncCallback 안에 내 활동에 WeakReference를 저장 한 후,myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

35

문제는 그 수 Activity있었다 finishedprogress of finishing.

확인을 추가 isFinishing하고이 경우에만 대화 상자를 닫습니다.false

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

isFinishing : 이 활동이 완료 중인지 확인하십시오. 호출 finish했거나 다른 사람이 완료를 요청했기 때문입니다.


1
점검으로 예외는 여전히 발생합니다
Marcos Vasconcelos

내 ProgressDialog는 활동이 아닌 클래스에 있으므로 isFinishing check @Libin
Maniraj

11

들어 DialogA의 생성 Fragment, 나는 다음과 같은 코드를 사용 :

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

이 패턴을 사용하여 Fragment가에서 분리 될 수 있는 경우를 처리 합니다 Activity.


8

코드가 어떻게 작동하는지보십시오 :

비동기 작업을 호출 한 후 백그라운드에서 비동기 작업이 실행됩니다. 그것은 바람직하다. 이제이 비동기 작업에는 코드를 보는 방법을 묻는 경우 활동에 첨부 된 진행률 대화 상자가 있습니다.

pDialog = new ProgressDialog(CLASS.this);

당신은 전달되는 Class.this인수에 컨텍스트한다. 따라서 진행 대화 상자가 여전히 활동에 첨부되어 있습니다.

이제 시나리오를 고려하십시오. 비동기 작업이 진행되는 동안 finish () 메소드를 사용하여 활동을 완료하려고하면 활동에 첨부 된 자원에 액세스하려고하는 시점이 progress bar됩니다 ( 예 : 활동이 더 이상 없을 때) 그곳에.

따라서 당신은 얻는다 :

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

이것에 대한 해결책 :

1) 활동이 완료되기 전에 대화 상자가 닫히거나 취소되었는지 확인하십시오.

2) 대화 상자가 닫히고 비동기 작업이 끝난 후에 만 ​​활동을 완료하십시오.


1
# 2와 관련하여 : 완료 시점을 완전히 제어 할 수는 없습니다 Activity. Android는 언제든지 완료 할 수 있습니다. 따라서 # 2는 말이되지 않습니다.
Alexander Abakumov

7

@erakitin 답변을 기반으로하지만 Android 버전 <API 레벨 17 과도 호환됩니다. Sadly Activity.isDestroyed () 는 API 레벨 17 이후에만 지원되므로 나처럼 이전 API 레벨을 대상으로하는 경우 직접 확인하십시오. View not attached to window manager그 후에 예외 가 없습니다 .

예제 코드

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


3

ConfigurationChanged에서 재정의하고 진행률 대화 상자를 닫습니다. 진행률 대화 상자가 세로로 작성되고 가로로 닫히면 창 관리자에 첨부되지 않은보기 오류가 발생합니다.

또한 진행률 표시 줄을 중지하고 onPause (), onBackPressed 및 onDestroy 메소드에서 비동기 작업을 중지하십시오.

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

    asyncTaskObj.cancel(true);

}

3

활동 제거시 무시하고 대화 상자를 닫고 null로 만듭니다.

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

3

첫째, 충돌 이유는 decorView의 색인이 -1이며, Android 소스 코드에서 알 수 있습니다. 코드 스 니펫이 있습니다.

클래스 : 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;
    }

그래서 우리는 추적 해상도를 얻습니다. decorView의 색인을 판단하십시오 .0보다 크면 계속하거나 그냥 돌아가서 해산을 포기하십시오.

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

2

다음 dismiss()과 같은 방법을 재정의하십시오 .

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

문제를 재현하려면 대화 상자를 닫기 전에 활동을 완료하십시오.


1

최고의 솔루션. 첫 번째 컨텍스트 확인은 활동 컨텍스트 또는 응용 프로그램 컨텍스트이며 활동 컨텍스트 인 경우 활동 만 완료되었는지 확인한 후 호출 dialog.show()또는dialog.dismiss();

아래의 샘플 코드를 참조하십시오 ... 도움이되기를 바랍니다!

디스플레이 대화 상자

if (context instanceof Activity) {
   if (!((Activity) context).isFinishing())
     dialog.show();
}

대화 상자 해제

if (context instanceof Activity) {
       if (!((Activity) context).isFinishing())
         dialog.dismiss();
    }

더 많은 검사를 추가하려면 조건 을 추가 dialog.isShowing()하거나 dialog !-null사용하십시오 &&.


0

이 문제는 해제 기능이 호출되기 전에 활동이 완료 되었기 때문에 발생합니다. 예외를 처리하고 정확한 이유로 ADB 로그를 확인하십시오.

/**
     * After completing background task Dismiss the progress dialog
     * **/
    protected void onPostExecute(String file_url) {
    try {
         if (pDialog!=null) {
            pDialog.dismiss();   //This is line 624!    
         }
    } catch (Exception e) {
        // do nothing
    }
     something(note);
}

0

이 예외를 재현 할 수있는 방법이 있습니다.

나는 2를 사용한다 AsyncTask. 하나는 긴 일을하고 다른 하나는 짧은 일을합니다. 짧은 작업이 완료되면에 전화하십시오 finish(). 긴 작업이 완료되고를 호출 Dialog.dismiss()하면 충돌이 발생합니다.

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

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected void onPreExecute() {
                mProgressDialog = ProgressDialog.show(MainActivity.this, "", "plz wait...", true);
            }

            @Override
            protected Void doInBackground(Void... nothing) {
                try {
                    Log.d(TAG, "long thread doInBackground");
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            protected void onPostExecute(Void result) {
                Log.d(TAG, "long thread onPostExecute");
                if (mProgressDialog != null && mProgressDialog.isShowing()) {
                    mProgressDialog.dismiss();
                    mProgressDialog = null;
                }
                Log.d(TAG, "long thread onPostExecute call dismiss");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

        new AsyncTask<Void, Void, Void>(){
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Log.d(TAG, "short thread doInBackground");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                Log.d(TAG, "short thread onPostExecute");
                finish();
                Log.d(TAG, "short thread onPostExecute call finish");
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

이것을 시도 하고이 문제를 해결하는 가장 좋은 방법을 찾을 수 있습니다. 내 연구에서 적어도 4 가지 방법으로 문제를 해결할 수 있습니다.

  1. @erakitin의 답변 : isFinishing()활동 상태를 확인하기 위해 전화
  2. @ Kapé의 답변 : 활동 상태를 확인하기 위해 플래그 설정
  3. try / catch를 사용하여 처리하십시오.
  4. 전화 AsyncTask.cancel(false)에서 onDestroy(). asynctask는 실행 onPostExecute()되지 않지만 onCancelled()대신 실행 됩니다.
    참고 : Android 2.XX와 같은 구형 Android OS에서 onPostExecute()전화를 걸더라도 여전히 실행됩니다 .AsyncTask.cancel(false)

가장 적합한 것을 선택할 수 있습니다.


0

pDialog를 전역 적으로 초기화 한 다음 제거하고보기 또는 대화 상자를 로컬로 초기화하십시오. 같은 문제가 있습니다.이 작업을 수행하면 문제가 해결됩니다. 그것이 당신을 위해 작동하기를 바랍니다.


0

우리는 또한 onPause방법이나 onDestroy방법 에 대한 대화를 닫습니다

@Override
protected void onPause() {
    super.onPause();
    dialog.dismiss();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.