Android View가 창 관리자에 연결되지 않았습니다.


111

다음 예외 중 일부가 있습니다.

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)

나는 그것을 봤고 그것이 팝업과 화면을 돌리는 것과 관련이 있음을 보았지만 내 코드에 대한 참조는 없습니다.

질문은 다음과 같습니다.

  1. 이 문제가 언제 발생하는지 정확히 알 수있는 방법이 있습니까?
  2. 화면을 돌리는 것 외에이 오류를 유발하는 다른 이벤트 나 조치가 있습니까?
  3. 이런 일이 발생하지 않도록 어떻게 방지합니까?

1
활동이 매니페스트에 어떻게 설명되어 있으며 오류가 발생했을 때 화면에 어떤 활동이 있는지 설명 할 수 있는지 확인합니다. 최소한의 테스트 케이스로 문제를 해결할 수 있는지 확인하십시오.
Josh Lee

View#onAttachedToWindow()호출 되기 전에보기를 수정하려고했을 수 있습니까?
Alex Lockwood

답변:


163

화면 방향 변경에서 진행률 대화 상자가있는 AsyncTask 전에 활동이 완료되는 문제가 발생했습니다. 대화 상자를 null로 설정 onPause()한 다음 해제하기 전에 AsyncTask에서이를 확인하여이 문제를 해결 한 것 같습니다 .

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

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

... 내 AsyncTask에서 :

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

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

12
@YekhezkelYovel AsyncTask는 활동이 다시 시작된 후에도 살아남은 스레드에서 실행 중이며 현재 죽은 활동 및 해당 뷰에 대한 참조를 보유 할 수 있습니다 (이는 사실상 단기적 일지라도 메모리 누수 임-작업 완료에 걸리는 시간에 따라 다름) ). 위의 솔루션은 단기 메모리 누수를 받아들이는 것이 만족 스러우면 잘 작동합니다. 권장되는 접근 방식은 활동이 일시 중지되었을 때 실행중인 AsyncTask를 cancel ()하는 것입니다.
Stevie

8

이 문제와 싸운 후 마침내이 해결 방법으로 끝납니다.

/**
 * 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;
    }
}

때로는이 문제에 대한 더 나은 해결책이 없다면 좋은 예외 처리가 잘 작동합니다.


5

Activity주위에 물체 가있는 경우 다음 isDestroyed()방법을 사용할 수 있습니다 .

Activity activity;

// ...

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

AsyncTask여러 장소에서 사용 하는 비 익명 서브 클래스가 있다면 좋습니다.


3

대화 상자를 만들고 숨기는 사용자 정의 정적 클래스를 사용하고 있습니다. 이 클래스는 하나의 활동뿐만 아니라 다른 활동에서도 사용되고 있습니다. 이제 당신이 묘사 한 문제도 나에게 나타 났고 나는 해결책을 찾기 위해 밤새 머물렀다 ..

마지막으로 해결책을 제시합니다!

대화 상자를 표시하거나 닫고 싶은데 터치하기 위해 대화를 시작한 활동을 모르는 경우 다음 코드가 적합합니다.

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


            }
        } 

 }

1

위의 솔루션은 나를 위해 작동하지 않았습니다. 그래서 제가 한 것은 ProgressDialog전 세계적으로 취한 다음 이것을 내 활동에 추가하는 것입니다.

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

활동이 파괴되면 ProgressDialog도 파괴됩니다.


0

질문 1) :

오류 메시지가 코드의 어떤 줄이 문제를 일으키는 지 알려주지 않는 것 같다는 점을 고려할 때 중단 점을 사용하여 추적 할 수 있습니다. 중단 점은 프로그램이 특정 코드 줄에 도달하면 프로그램 실행을 일시 중지합니다. 중요한 위치에 중단 점을 추가하여 충돌을 일으키는 코드 줄을 확인할 수 있습니다. 예를 들어 프로그램이 setContentView () 행에서 충돌하는 경우 여기에 중단 점을 넣을 수 있습니다. 프로그램이 실행되면 해당 줄을 실행하기 전에 일시 중지됩니다. 재개로 인해 다음 중단 점에 도달하기 전에 프로그램이 중단되면 프로그램을 종료 한 줄이 두 중단 점 사이에 있다는 것을 알 수 있습니다.

Eclipse를 사용하는 경우 중단 점을 추가하는 것은 쉽습니다. 코드 바로 왼쪽 여백을 마우스 오른쪽 버튼으로 클릭하고 "Toggle breakpoint"를 선택합니다. 그런 다음 일반 실행 버튼 옆에있는 녹색 곤충 모양의 버튼 인 디버그 모드에서 애플리케이션을 실행해야합니다. 프로그램이 중단 점에 도달하면 Eclipse는 디버그 퍼스펙티브로 전환하고 대기중인 라인을 표시합니다. 프로그램 실행을 다시 시작하려면 일반 '재생'처럼 보이지만 삼각형 왼쪽에 세로 막대가있는 '다시 시작'버튼을 찾으십시오.

또한 애플리케이션을 Log.d ( "My application", "여기에 로그 행이있는 위치를 알려주는 정보")로 채우면 Eclipse의 LogCat 창에 메시지가 게시됩니다. 해당 창을 찾을 수없는 경우 창->보기 표시-> 기타 ...-> Android-> LogCat을 사용하여 엽니 다.

도움이 되었기를 바랍니다.


7
이 문제는 클라이언트 전화에서 발생하므로 디버깅 옵션이 없습니다. 또한 문제는 항상 발생하고 가끔 발생하므로 추적 방법을 모릅니다
Daniel Benedykt

손을 잡을 수 있다면 전화기에서 디버깅 메시지를받을 수 있습니다. 메뉴-> 설정-> 애플리케이션-> 개발에는 USB 디버깅 옵션이 있습니다. 활성화 된 경우 전화기를 연결하면 LogCat이 모든 일반 디버깅 라인을 포착합니다. 그 외에는 ... 음 ... 프로그램의 상태에 따라 간헐적 인 현상이 설명 될 수 있습니다. 문제를 재현 할 수 있는지 확인하기 위해 프로그램 사용을 엉망으로 만들어야한다고 생각합니다.
Steve Haley

4
이전에 말했듯이 문제는 클라이언트 전화에서 발생하고 있으며 액세스 권한이 없습니다. 클라이언트가 다른 대륙에있을 수 있습니다. :)
Daniel Benedykt

logcat을 이메일로 보내는 앱이 시장에 나와 있습니다.
Tim Green

1
숫자 패드 키를 눌러 에뮬레이터를 회전 할 수 있습니다. 9
Rob

0

해당 활동의 매니페스트에 다음을 추가했습니다.

android:configChanges="keyboardHidden|orientation|screenLayout"

19
이것은 해결책이 아닙니다. 시스템이 다른 이유로 인해 활동을 파괴 할 수도 있습니다.
Roel 2015 년

1
당신의 대답을 설명해 주시겠습니까?
Leebeedev 2015 년

0

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

여기에 작성한 코드는 Google이하는 일입니다. 내가 쓴 것은이 문제의 원인을 보여주기 위해서입니다.
안드로이드 개발자

0

내 문제는 내 안드로이드에서 화면 회전을 uhlock하여 해결되었습니다. 문제를 일으키는 앱이 이제 완벽하게 작동합니다.


0

또 다른 옵션은 대화 상자에서 onAttachedToWindow ()를 재정 의하여 대화 상자가 창에 연결될 때까지 비동기 작업을 시작하지 않는 것입니다. 이렇게하면 항상 닫을 수 있습니다.


0

또는 간단히 추가 할 수 있습니다.

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

하게 할 ProgressDialog수 없음으로 취소-수를


-2

다음과 같이 catch를 시도해보십시오.

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

IllegalStateOfException 비정상적으로 작동하는 것이 아니라 더 나은 방식으로 처리되어야합니다.
Lavakush

-3

매니페스트에서 활동을 선언 할 때 android : configChanges = "orientation"이 필요합니다.

예:

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

1
이 태그는 개발자가 Android 시스템에서 활동을 파괴하고 다시 생성하지 않으려는 경우에만 필요합니다.
Ankit 2013
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.