Xem không được đính kèm với sự cố quản lý cửa sổ


188

Tôi đang sử dụng ACRA để báo cáo sự cố ứng dụng. Tôi đã nhận được một View not attached to window managerthông báo lỗi và nghĩ rằng tôi đã sửa nó bằng cách gói pDialog.dismiss();câu lệnh if:

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

Nó đã giảm số lượng View not attached to window managersự cố tôi nhận được, nhưng tôi vẫn nhận được một số và tôi không biết làm thế nào để giải quyết nó.

Thông báo lỗi:

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)

Đoạn mã:

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

Rõ ràng:

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

Tôi đang thiếu gì để ngăn chặn vụ tai nạn này xảy ra?


1
Bạn đã bao giờ tìm ra cái này chưa? Im có cùng một vấn đề. Dường như không thể tìm ra nó.
tomjung

Tiếc là không có. Tôi có thể bắt đầu một tiền thưởng trong một chút. Bạn nên kiểm tra một số chủ đề khác liên quan đến vấn đề như vậy trong trường hợp chúng giúp bạn.
Howli

Của bạn AsyncTaskđược tuyên bố bên trong Activityhay Fragment?
erakitin

Vui lòng gửi các hàm "doMoreStuff ()" và "Something ()".
điên cuồng

Vấn đề có thể do quá nhiều công việc của luồng chính, vì vậy hãy thử sử dụng các trình xử lý để hiển thị tiến trình và nếu (pDialog! = Null) thì dòng này không cần thiết, bởi vì chính nó sẽ kiểm tra xem hộp thoại có đang được xử lý hay không.
Madhu

Câu trả lời:


446

Cách tái tạo lỗi:

  1. Kích hoạt tùy chọn này trên thiết bị của bạn : Settings -> Developer Options -> Don't keep Activities.
  2. Nhấn nút Home trong khi AsyncTaskđang thực thi và ProgressDialogđang hiển thị.

Hệ điều hành Android sẽ hủy một hoạt động ngay khi nó bị ẩn. Khi onPostExecuteđược gọi là Activitysẽ ở trạng thái "hoàn thiện"ProgressDialogsẽ không được đính kèm Activity.

Cách khắc phục:

  1. Kiểm tra trạng thái hoạt động trong onPostExecutephương pháp của bạn .
  2. Loại bỏ phương pháp ProgressDialogtrong onDestroy. Nếu không, android.view.WindowLeakedngoại lệ sẽ được ném. Ngoại lệ này thường xuất phát từ các hộp thoại vẫn còn hoạt động khi hoạt động kết thúc.

Hãy thử mã cố định này:

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

8
Câu trả lời chính xác! BTW, isShowing () là không cần thiết bởi vì notify () sẽ không làm gì nếu isShowing () == false. Mã nguồn
Peter Zhao

3
Lợi ích của việc sử dụng là gì isDestroyed()qua isFinishing()trên tất cả các API cho mục đích cụ thể này?
Alexander Abakumov

@AlexanderAbakumov: Từ những gì tôi hiểu isFinishing()không được đảm bảo truenếu hoạt động bị hệ thống phá hủy, hãy xem tài liệu về Hoạt động .
Markus Penguin

1
@MarkusPenguin: Phải. Nhưng, trong trường hợp này, nếu một người cố gắng sử dụng một lời khuyên từ nhận xét của tác giả // or call isFinishing() if min sdk version < 17, anh ta sẽ gặp phải trường hợp ngoại lệ tương tự. Vì vậy, chúng tôi cần một giải pháp khác với câu trả lời này cho các ứng dụng chạy trên API <17.
Alexander Abakumov

@AlexanderAbakumov Tôi đang gặp vấn đề tương tự, nhưng đang hoàn thành nó không hiệu quả với tôi, Nó hoạt động tiết kiệm WeakReference cho hoạt động của tôi bên trong AsyncCallback, và sau đó:myActivityWeakReference.get() != null && !myActivityWeakReference.get().isFinishing()
rusito23

34

Vấn đề có thể là Activityđã finishedhoặc đang ở progress of finishing.

Thêm một kiểm tra isFinishingvà bỏ qua hộp thoại chỉ khi điều này làfalse

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

isFinishing: Kiểm tra xem liệu hoạt động này đang trong quá trình hoàn thiện hay chưa, vì bạn đã gọi finishnó hoặc ai đó đã yêu cầu nó kết thúc.


1
Với tấm séc, ngoại lệ vẫn đang bị ném
Marcos Vasconcelos

ProgressDialog của tôi ở trong một lớp không phải là một hoạt động nên tôi không thể sử dụng kiểm tra isFinishing @Libin
Maniraj

10

Để Dialogtạo trong một Fragment, tôi sử dụng mã sau đây:

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

Tôi sử dụng mô hình này để giải quyết trường hợp khi một Fragmentcó thể được tách ra khỏi Activity.


8

Xem cách Mã hoạt động ở đây:

Sau khi gọi tác vụ Async, tác vụ async sẽ chạy trong nền. đó là mong muốn Bây giờ, tác vụ Async này có hộp thoại tiến trình được đính kèm với Hoạt động, nếu bạn hỏi cách xem mã:

pDialog = new ProgressDialog(CLASS.this);

Bạn đang chuyển Class.thisbối cảnh như đối số. Vì vậy, hộp thoại Tiến trình vẫn được gắn vào hoạt động.

Bây giờ hãy xem xét kịch bản: Nếu chúng tôi cố gắng hoàn thành hoạt động bằng cách sử dụng phương thức finish (), trong khi tác vụ async đang diễn ra, là điểm mà bạn đang cố truy cập Tài nguyên được gắn vào hoạt động, tức là progress barkhi hoạt động không còn nữa ở đó

Do đó bạn nhận được:

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

Giải pháp cho vấn đề này:

1) Đảm bảo rằng hộp thoại bị loại bỏ hoặc hủy trước khi hoạt động kết thúc.

2) Kết thúc hoạt động, chỉ sau khi hộp thoại bị loại bỏ, đó là nhiệm vụ không đồng bộ kết thúc.


1
Về # 2: bạn không có toàn quyền kiểm soát khi nào hoàn thành Activity; Android có thể hoàn thành nó bất cứ lúc nào. Vì vậy, # 2 không có ý nghĩa.
Alexander Abakumov

6

Dựa trên câu trả lời @erakitin, nhưng cũng tương thích với các phiên bản Android <API cấp 17. Đáng buồn là Activity.isDestroyed () chỉ được hỗ trợ kể từ cấp API 17, vì vậy, nếu bạn nhắm mục tiêu cấp API cũ hơn như tôi, bạn sẽ phải tự kiểm tra Sau đó không có View not attached to window managerngoại lệ.

Mã ví dụ

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

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

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

tham khảo điều này .


3

Ghi đè lên hộp thoại tiến trình thay đổi và bỏ qua tiến trình. Nếu hộp thoại tiến trình được tạo theo chiều dọc và bỏ qua theo chiều ngang thì nó sẽ ném View không được đính kèm với lỗi trình quản lý cửa sổ.

Đồng thời dừng thanh tiến trình và dừng tác vụ async trong phương thức onPause (), onBackPression và onDestroy.

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

    asyncTaskObj.cancel(true);

}

3

Ghi đè onDestroy của Hoạt động và loại bỏ Hộp thoại của bạn và biến nó thành null

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

3

Thứ nhất, lý do sự cố là chỉ số của decorView là -1, chúng ta có thể biết nó từ mã nguồn Android, có đoạn mã:

lớp: android.view.WindowManagerGlobal

tệp: 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;
    }

để chúng tôi có được độ phân giải, chỉ cần phán đoán chỉ số của decorView, nếu nó nhiều hơn 0 thì tiếp tục hoặc chỉ trả về và từ bỏ, mã như sau:

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

Giám sát dismiss()phương thức như thế này:

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

Để tái tạo vấn đề, chỉ cần kết thúc hoạt động trước khi bỏ qua hộp thoại.


1

giải pháp tốt nhất. Kiểm tra bối cảnh đầu tiên là bối cảnh hoạt động hoặc bối cảnh ứng dụng nếu bối cảnh hoạt động sau đó chỉ kiểm tra hoạt động đã kết thúc hay chưa thì gọi dialog.show()hoặcdialog.dismiss();

Xem mã mẫu bên dưới ... hy vọng nó sẽ hữu ích!

Hộp thoại hiển thị

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

Hộp thoại loại bỏ

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

Nếu bạn muốn thêm nhiều kiểm tra thì thêm dialog.isShowing()hoặc dialog !-nullsử dụng &&điều kiện.


0

Vấn đề này là do hoạt động của bạn kết thúc trước khi chức năng loại bỏ được gọi. Xử lý ngoại lệ và kiểm tra nhật ký ADB của bạn để biết lý do chính xác.

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

Tôi có một cách để tái tạo ngoại lệ này.

Tôi sử dụng 2 AsyncTask. Một người làm nhiệm vụ dài và người khác làm nhiệm vụ ngắn. Sau khi hoàn thành nhiệm vụ ngắn, gọi finish(). Khi nhiệm vụ dài hoàn thành và gọi Dialog.dismiss(), nó gặp sự cố.

Đây là mã mẫu của tôi:

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

Bạn có thể thử điều này và tìm ra cách tốt nhất để khắc phục vấn đề này. Từ nghiên cứu của tôi, có ít nhất 4 cách để khắc phục:

  1. @ erakitin câu trả lời: gọi isFinishing()để kiểm tra trạng thái của hoạt động
  2. @ Câu trả lời của Kapé: đặt cờ để kiểm tra trạng thái của hoạt động
  3. Sử dụng thử / bắt để xử lý nó.
  4. Gọi AsyncTask.cancel(false)trong onDestroy(). Nó sẽ ngăn không cho asynctask thực thi onPostExecute()nhưng onCancelled()thay vào đó thực thi .
    Lưu ý: onPostExecute()vẫn sẽ thực thi ngay cả khi bạn gọi AsyncTask.cancel(false)trên HĐH Android cũ hơn, như Android 2.XX

Bạn có thể chọn một cái tốt nhất cho bạn.


0

Có thể bạn khởi tạo pDialog trên toàn cầu, sau đó xóa nó và hiển thị nội dung hoặc hộp thoại của bạn cục bộ. Tôi có cùng một vấn đề, tôi đã làm điều này và vấn đề của tôi đã được giải quyết. Hy vọng nó sẽ làm việc cho u.


0

chúng tôi cũng đã bỏ qua hộp thoại của chúng tôi về onPausephương pháp hoặc onDestroyphương pháp

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

@Override
protected void onDestroy() {
    super.onDestroy();
    dialog.dismiss();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.