programing

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

padding 2023. 10. 5. 21:16
반응형

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

저는 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. 단말기에서 다음 옵션을 사용하도록 설정합니다.Settings -> Developer Options -> Don't keep Activities.
  2. AsyncTask이며,ProgressDialog상영 중입니다.

Android OS는 활동을 숨기자마자 이를 파괴합니다..onPostExecute다이라고 .Activity"finishing" 상태에 있을 것이고ProgressDialog.Activity.

수정 방법:

  1. 합니다에서 합니다.onPostExecute방법.
  2. 합니다를 합니다.ProgressDialog인에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);
        }
    }
}

가 될 수 Activity가본 적 있다finished아니면progress of finishing.

isFinishing합니다.false

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

is Finishing : 합니다에 활동이 합니다. 전화를 거셨기 때문에.finish다른 사람이 완료를 요청했습니다.

위해서DialogFragment합니다

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

는 이 하여 합니다 시합니다.Fragment다에서 될 수 .Activity.

Code가 어떻게 작동하는지 여기에서 확인하십시오.

비동기 작업을 호출한 후 비동기 작업이 백그라운드에서 실행됩니다.그것이 바람직합니다.이제 이 비동기 작업에는 Activity(활동)에 첨부된 진행 대화상자가 있습니다. 코드를 보는 방법을 묻는다면 다음과 같습니다.

pDialog = new ProgressDialog(CLASS.this);

은 하고 있습니다.Class.this논쟁의 맥락으로서 ) 대화 는 여전히 .따라서 Progress(진행률) 대화 상자가 여전히 활동에 연결되어 있습니다.

이제 시나리오를 생각해 보겠습니다.), 즉다 Finish()이 됩니다.progress bar더 이상 활동이 없을 때 말입니다.

따라서 다음을 얻을 수 있습니다.

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

이에 대한 해결책:

1) 활동이 완료되기 전에 대화상자가 삭제되거나 취소되었는지 확인합니다.

2) 대화상자가 종료된 후에만 활동을 종료합니다. 즉, 비동기 작업이 종료됩니다.

@erakitin answer 기준이지만 Android 버전 < API level 17에도 호환됩니다.안타깝게도 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
        }
    }
}
@Override
public void onPause() {
    super.onPause();

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

이것을 참고하세요.

ConfigurationChanged에서 재정의하고 진행률 대화 상자를 취소합니다.진행률 대화상자가 세로로 작성되고 가로로 삭제되면 View not attached to window manager 오류가 발생합니다.

또한 진행 표시줄을 중지하고 일시 중지(), BackPressed 및 destroy 메서드에서 비동기 작업을 중지합니다.

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

    asyncTaskObj.cancel(true);

}

활동 파기 및 대화 상자 해제를 재정의하고 null로 만듭니다.

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

dismiss()방법은 다음과 같습니다.

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

문제를 재생하려면 해제 대화 상자를 열기 전에 작업을 마치기만 하면 됩니다.

먼저 충돌 이유는 decoView의 인덱스는 -1이고, 안드로이드 소스 코드를 통해 알 수 있으며, 코드 스니펫이 있습니다.

class:android.view.WindowsManagerGlobal

파일: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;
    }

따라서 우리는 해결책을 따르고 decView의 인덱스를 판단하여 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();
        }

이 문제는 해제 기능이 호출되기 전에 작업이 완료되기 때문입니다.예외를 처리하고 정확한 이유가 무엇인지 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);
}

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

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

이 문제를 해결하는 가장 좋은 방법이 무엇인지 확인할 수 있습니다.제 연구에 의하면, 적어도 네 가지 해결 방법이 있습니다.

  1. @erakitin 의: Call @erakitinisFinishing()
  2. @Kapé의 답: 활동 상태를 확인할 수 있는 플래그 설정
  3. Try/Catch를 사용하여 처리합니다.
  4. .AsyncTask.cancel(false)인에onDestroy().을(를) 비동기 작업을 할 수 .onPostExecute()행을 합니다.onCancelled()대신.
    고:onPostExecute()AsyncTask.cancel(false)안드로이드 2.X.X와 같은 오래된 Android OS에서.

당신은 당신에게 가장 적합한 것을 선택할 수 있습니다.

최선의 해결책.인지 또는 하고 활동이 만 확인한 합니다.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&&조건.

는 또한 했습니다에 했습니다.onPauseronDestroyd

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

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

pDialog를 전역적으로 초기화한 다음 pDialog를 제거하고 로컬에서 보기 또는 대화 상자를 초기화할 수 있습니다.저도 같은 문제가 있어요, 제가 이 일을 했고 제 문제는 해결되었습니다.당신에게 효과가 있기를 바랍니다.

솔루션을 확인하거나 아래 솔루션을 사용해 볼 수 있습니다.

if (pDialog instanceof Activity && !((Activity) mContext).isFinishing())
        pDialog.show();

소유자 활동이 아직 활성화되어 있는지 확인합니다.

if (dialog.getOwnerActivity() ==null || dialog.getOwnerActivity().isFinishing()) {
    dialog.dismiss();
}

저는 이 간단한 확장 기능을 사용하여 활동이 파괴되는지 여부를 확인하고 있습니다.

    protected fun Activity?.isNoMore(): Boolean {
        return this?.let {
            isFinishing || isDestroyed
        } ?: true
    }

아래에 언급된 것처럼 활동에 사용합니다.

if (!isNoMore()) {
//show your dialog. Also, make the null check for dialog object.
}

제안된 솔루션 중 어떤 것도 사용자 지정 Dialog 클래스를 사용하고 있고 모든 활동에서 중지/파괴 방법을 무시하고 싶지 않기 때문에 클래스에 매개 변수를 하나 더 추가하여 인스턴스에서 전송하는 것이 더 쉽습니다. 이 솔루션은 저에게 적합합니다.

class CustomLoadingClass(var activity: Activity?, var lifecycle: Lifecycle? = null) {

lateinit var dialog: Dialog

fun show(cancelable: Boolean = false, title: String? = null) {
    lifecycle?.let {
        val defaultLifecycleObserver = object : DefaultLifecycleObserver {
            override fun onCreate(owner: LifecycleOwner) {
                super.onCreate(owner)
                activity?.let { it ->
                    if (!it.isFinishing && !it.isDestroyed) {
                        dialog = Dialog(it, android.R.style.Theme_Translucent_NoTitleBar)
                        dialog.setContentView(it.layoutInflater.inflate(R.layout.full_screen_progress_bar,null)!!)
                        dialog.setCancelable(cancelable)
                        dialog.show()
                    }
                }
            }
            override fun onPause(owner: LifecycleOwner) {
                super.onPause(owner)
                dismiss()
            }

            override fun onStop(owner: LifecycleOwner) {
                super.onStop(owner)
                dismiss()
            }

            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                dismiss()
            }
        }
        it.addObserver(defaultLifecycleObserver)
    }
}

fun dismiss() {
    activity?.let { it ->
         if (!it.isFinishing && !it.isDestroyed) 
           dialog.dismiss()
    }
  }
}

if (progressDialog.is Showing() & progressDialog != null){ progressDialog.dismiss();

언급URL : https://stackoverflow.com/questions/22924825/view-not-attached-to-window-manager-crash

반응형