“Android.view.WindowManager $ BadTokenException: Không thể thêm cửa sổ” trên buider.show ()


118

Từ main của tôi activity, tôi cần gọi một lớp bên trong và trong một phương thức bên trong lớp, tôi cần hiển thị AlertDialog. Sau khi loại bỏ nó, khi nút OK được nhấn, hãy chuyển tiếp đến Google Play để mua.

Mọi thứ hoạt động hoàn hảo trong hầu hết thời gian, nhưng đối với một số người dùng, nó đang bị lỗi builder.show()và tôi có thể thấy "android.view.WindowManager$BadTokenException:Không thể thêm cửa sổ "từ nhật ký sự cố. Vui lòng đề xuất.

Mã của tôi khá giống thế này:

public class classname1 extends Activity{

  public void onCreate(Bundle savedInstanceState) {
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.<view>); 

    //call the <className1> class to execute
  }

  private class classNamename2 extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {}

    protected void onPostExecute(String result){
      if(page.contains("error")) 
      {
        AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
        builder.setCancelable(true);
        builder.setMessage("");
        builder.setInverseBackgroundForced(true);
        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
            if(!<condition>)
            {
              try
              {
                String pl = ""; 

                mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                  <listener>, pl);
              }

              catch(Exception e)
              {
                e.printStackTrace();
              }
            }  
          }
        });

        builder.show();
      }
    }
  }
}

Tôi cũng đã thấy lỗi trong một cảnh báo khác mà tôi không chuyển tiếp đến bất kỳ cảnh báo nào khác activity. Nó đơn giản như thế này:

AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
    builder.setCancelable(true);

    //if successful
    builder.setMessage(" ");
    builder.setInverseBackgroundForced(true);
    builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton){
            // dialog.dismiss();
                   }
    });
    builder.show();
}

2
Nếu đây là mã hoàn chỉnh của bạn, bạn có thực sự cần AsyncTask không?
Shobhit Puri

Đây không phải là mã hoàn chỉnh, đây là khá một mã lớn vì vậy tôi chỉ thêm phần ở đây, nơi tôi nhìn thấy vấn đề từ báo cáo tai nạn
MSIslam

được rồi Tốt. Thông thường, bạn chỉ có thể đăng tên chức năng và nhận xét rằng bạn đang làm nhiều thứ ở đó (như bạn đã làm bây giờ). Nó dễ hiểu hơn. :).
Shobhit Puri

Bạn có đang điều hướng đến một số hoạt động khác từ hoạt động này ở đâu đó không?
Shobhit Puri

1
Bạn đã viết là bình luận //send to some other activity. Tôi nghĩ rằng nếu bạn nhận xét phần bạn đang đi đến Hoạt động mới, lỗi này sẽ biến mất. Lỗi dường như xảy ra vì hộp thoại trước của bạn bị loại bỏ hoàn toàn, hoạt động mới của bạn bắt đầu. Trong onPostExecute(), bạn có hộp thoại cảnh báo và bạn đang đưa ra ngữ cảnh của loginHoạt động. Nhưng bạn đang điều hướng đến hoạt động khác, vì vậy ngữ cảnh trở nên sai. Do đó bạn đang gặp lỗi này! Xem câu hỏi tương tự stackoverflow.com/questions/15104677/… .
Shobhit Puri

Câu trả lời:


264
android.view.WindowManager$BadTokenException: Unable to add window"

Vấn đề :

Ngoại lệ này xảy ra khi ứng dụng đang cố gắng thông báo cho người dùng từ chuỗi nền (AsyncTask) bằng cách mở Hộp thoại.

Nếu bạn đang cố gắng sửa đổi giao diện người dùng từ chuỗi nền (thường từ onPostExecute () của AsyncTask) và nếu hoạt động bước vào giai đoạn hoàn thiện, tức là) gọi điện thoại finish () một cách rõ ràng, người dùng nhấn nút home hoặc back hoặc hoạt động dọn dẹp do Android thực hiện thì bạn nhận được lỗi này.

Lý do:

Lý do cho ngoại lệ này là, như thông báo ngoại lệ cho biết, hoạt động đã kết thúc nhưng bạn đang cố gắng hiển thị hộp thoại có ngữ cảnh của hoạt động đã kết thúc. Vì không có cửa sổ cho hộp thoại để hiển thị thời gian chạy android nên ngoại lệ này.

Giải pháp:

Sử dụng isFinishing()phương thức được Android gọi để kiểm tra xem hoạt động này có đang trong quá trình hoàn tất hay không: có thể là lệnh gọi finish () rõ ràng hoặc hoạt động được Android thực hiện. Bằng cách sử dụng phương pháp này, rất dễ dàng tránh mở hộp thoại từ chuỗi nền khi hoạt động kết thúc.

Đồng thời duy trì một weak referencecho hoạt động (và không phải là một tham chiếu mạnh để hoạt động có thể bị hủy một khi không cần thiết) và kiểm tra xem hoạt động có chưa kết thúc trước khi thực hiện bất kỳ giao diện người dùng nào bằng cách sử dụng tham chiếu hoạt động này (tức là hiển thị hộp thoại).

vd .

private class chkSubscription extends AsyncTask<String, Void, String>{

  private final WeakReference<login> loginActivityWeakRef;

  public chkSubscription (login loginActivity) {
    super();
    this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
  }

  protected String doInBackground(String... params) {
    //web service call
  }

  protected void onPostExecute(String result) {
    if(page.contains("error")) //when not subscribed
    {
      if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
        builder.setCancelable(true);
        builder.setMessage(sucObject);
        builder.setInverseBackgroundForced(true);

        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
          }
        });

        builder.show();
      }
    }
  }
}

Cập nhật:

Mã thông báo cửa sổ:

Như tên của nó, mã thông báo cửa sổ là một loại mã thông báo Binder đặc biệt mà trình quản lý cửa sổ sử dụng để xác định duy nhất một cửa sổ trong hệ thống. Các mã thông báo cửa sổ rất quan trọng đối với bảo mật vì chúng giúp các ứng dụng độc hại không thể xâm nhập vào cửa sổ của các ứng dụng khác. Trình quản lý cửa sổ bảo vệ chống lại điều này bằng cách yêu cầu các ứng dụng chuyển mã thông báo cửa sổ của ứng dụng của họ như một phần của mỗi yêu cầu thêm hoặc xóa cửa sổ. Nếu các mã không khớp, trình quản lý cửa sổ sẽ từ chối yêu cầu và ném BadTokenException . Nếu không có mã thông báo cửa sổ, bước nhận dạng cần thiết này sẽ không thể thực hiện được và trình quản lý cửa sổ sẽ không thể tự bảo vệ khỏi các ứng dụng độc hại.

 Một kịch bản trong thế giới thực:

Khi một ứng dụng khởi động lần đầu tiên,  ActivityManagerService  tạo một loại mã thông báo cửa sổ đặc biệt được gọi là mã thông báo cửa sổ ứng dụng, nó xác định duy nhất cửa sổ vùng chứa cấp cao nhất của ứng dụng. Trình quản lý hoạt động cấp mã thông báo này cho cả ứng dụng và trình quản lý cửa sổ, đồng thời ứng dụng sẽ gửi mã thông báo đến trình quản lý cửa sổ mỗi khi nó muốn thêm cửa sổ mới vào màn hình. Điều này đảm bảo sự tương tác an toàn giữa ứng dụng và trình quản lý cửa sổ (bằng cách không thể thêm cửa sổ lên trên các ứng dụng khác) và cũng giúp người quản lý hoạt động dễ dàng đưa ra yêu cầu trực tiếp với trình quản lý cửa sổ.


Điều này có rất nhiều ý nghĩa! Ngoài ra giải pháp của bạn nghe có vẻ tuyệt vời đối với tôi. (y)
MSIslam

Thông báo 'Trường cuối cùng trống loginActivityWeakRef có thể chưa được khởi tạo' và thử theo cách này: private final WeakReference <login> loginActivityWeakRef = new WeakReference <login> (login.this); không chắc chắn nếu đó là điều đúng đắn nên làm
MSIslam

Ngoài ra, tôi đã loại bỏ cuối cùng trước WeakReference <login> loginActivityWeakRef vì nó hiển thị lỗi trong hàm tạo.
MSIslam

1
thử sử dụng chkCubscription mới (this) .execute (""); thay vì chkCubscription.execute ("") mới; như bạn đã đăng ở trên.
Ritesh Gune

2
Lỗi kinh khủng !! Tôi đang làm theo một hướng dẫn và với tư cách là @PhilRoggenbuck, vấn đề của tôi là do gọi Toast..Show () ngay trước khi gọi StartActivity (...). Để khắc phục, tôi đã di chuyển bánh mì nướng trong hoạt động mới được gọi là thay thế!
Thierry

26

Tôi đã có hộp thoại hiển thị chức năng:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}

Tôi gặp lỗi này và tôi chỉ cần kiểm tra isFinishing()trước khi gọi chức năng hiển thị hộp thoại này.

if(!isFinishing())
    showDialog();

1
chúng ta không nên viết if(!MyActivity.this.isFinishing())? Nếu nó không phải là ngay trong MyActivity
Bibaswann Bandyopadhyay

2
Tại sao Android sẽ chạy bất kỳ mã nào nếu nó đã hoàn tất? Nếu chúng ta làm theo giải pháp này thì hãy tưởng tượng bao nhiêu thời gian chúng ta thực sự nên sử dụng isFinishing để tránh các vấn đề tương tự.
David

@David Tôi nghĩ rằng điều này còn thiếu một vài chi tiết, chẳng hạn như hộp thoại được gọi trong một chuỗi nền, nhưng tôi hoàn toàn đồng ý với quan điểm của bạn như hiện tại.
Giỏ hàng bị bỏ rơi

Điểm tuyệt vời, tại sao tôi cần kiểm tra isFinishing!
Chibueze Opata

9

Lý do có thể là bối cảnh của hộp thoại cảnh báo. Bạn có thể đã kết thúc hoạt động đó nên nó đang cố gắng mở trong ngữ cảnh đó nhưng hoạt động đã bị đóng. Hãy thử thay đổi ngữ cảnh của hộp thoại đó thành hoạt động đầu tiên của bạn vì nó sẽ không kết thúc cho đến khi kết thúc.

ví dụ

hơn là điều này.

AlertDialog alertDialog = new AlertDialog.Builder(this).create();

thử sử dụng

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();

3
  • đầu tiên bạn không thể mở rộng AsyncTask mà không ghi đè doInBackground
  • thứ hai, hãy thử tạo AlterDailog từ trình tạo rồi gọi show ().

    private boolean visible = false;
    class chkSubscription extends AsyncTask<String, Void, String>
    {
    
        protected void onPostExecute(String result)
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setCancelable(true);
            builder.setMessage(sucObject);
            builder.setInverseBackgroundForced(true);
            builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton)
                {
                    dialog.dismiss();
                }
            });
    
            AlertDialog myAlertDialog = builder.create();
            if(visible) myAlertDialog.show();
        }
    
        @Override
        protected String doInBackground(String... arg0)
        {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        visible = true;
    }
    
    @Override
    protected void onStop()
    {
        visible = false; 
        super.onStop();
    }

1
Cảm ơn vì câu trả lời. Tôi thực sự đã sử dụng phương thức doInBackground, chỉ không đề cập đến nó ở đây vì nó không liên quan đến cảnh báo. Về việc thêm builder.create (), nó có vẻ hoạt động tốt, nhưng không biết liệu nó có hoạt động tốt cho mọi người hay không. Như tôi đã nói trước đó, mã hiện tại của tôi cũng hoạt động tốt, nhưng chỉ một vài lần đối với một số ít người dùng, nó cho thấy sự cố không thể thêm cửa sổ. Bạn có thể vui lòng gợi ý cho tôi những gì có thể là vấn đề thực sự trong mã hóa của tôi, điều gì có thể gây ra điều này?
MSIslam

trong trường hợp này, người dùng thoát khỏi hoạt động của bạn trước khi onPostExecute được gọi, vì vậy không có cửa sổ nào để giữ hộp thoại và điều này khiến ứng dụng của bạn gặp sự cố. thêm cờ vào onStop để biết nếu hoạt động của bạn không hiển thị nữa thì không hiển thị hộp thoại.
moh.sukhni

onPostExecute thực sự được gọi, vì builder.show () ở trong một điều kiện khi tôi đang kiểm tra xem người dùng chưa được đăng ký hay chưa dựa trên kết quả cuộc gọi dịch vụ web từ doInBackground (). Vì vậy, nếu onPostExecute không được gọi, nó sẽ không xuất hiện dòng builder.show ().
MSIslam

onPostExecute được gọi theo mặc định sau doInBackground, bạn không thể gọi nó và những gì sẽ được thực thi.
moh.sukhni

1
tác vụ không đồng bộ của bạn sẽ tiếp tục hoạt động sau khi người dùng điều hướng khỏi hoạt động của bạn và điều này sẽ khiến builder.show () nhồi nhét ứng dụng của bạn vì không có hoạt động nào để xử lý giao diện người dùng cho bạn. vì vậy ứng dụng của bạn đang lấy dữ liệu từ web nhưng hoạt động của bạn đã bị phá hủy trước khi bạn nhận được dữ liệu.
moh.sukhni

1

Tôi đang tạo Dialog trong onCreatevà sử dụng nó với showhide. Đối với tôi, nguyên nhân gốc rễ không nằm ở việc sa thải onBackPressed, mà là kết thúc Homehoạt động.

@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

Tôi đang hoàn thành Hoạt động trên trang chủ onBackPressedmà không đóng / loại bỏ hộp thoại của mình.

Khi tôi loại bỏ các hộp thoại của mình, sự cố đã biến mất.

new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

0

Tôi thử nó đã được giải quyết.

 AlertDialog.Builder builder = new AlertDialog.Builder(
                   this);
            builder.setCancelable(true);
            builder.setTitle("Opss!!");

            builder.setMessage("You Don't have anough coins to withdraw. ");
            builder.setMessage("Please read the Withdraw rules.");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("OK",
                    (dialog, which) -> dialog.dismiss());
            builder.create().show();

-1

Thử cái này :

    public class <class> extends Activity{

    private AlertDialog.Builder builder;

    public void onCreate(Bundle savedInstanceState) {
                    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
                    super.onCreate(savedInstanceState);

                setContentView(R.layout.<view>); 

                builder = new AlertDialog.Builder(<class>.this);
                builder.setCancelable(true);
                builder.setMessage(<message>);
                builder.setInverseBackgroundForced(true);

        //call the <className> class to execute
}

    private class <className> extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {

    }
    protected void onPostExecute(String result){
        if(page.contains("error")) //when not subscribed
        {   
           if(builder!=null){
                builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton){
                    dialog.dismiss();
                        if(!<condition>)
                        {
                        try
                        {
                        String pl = ""; 

                        mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                        <listener>, pl);
                        }

                        catch(Exception e)
                        {
                        e.printStackTrace();
                        }
                    }  
                }
            });

            builder.show();
        }
    }

}
}

-4

với ý tưởng biến toàn cầu này, tôi đã lưu cá thể MainActivity trong onCreate (); Biến toàn cầu của Android

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

và Mở hộp thoại như thế này. nó đã làm việc.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Global Var
    globals = (ApplicationController) this.getApplication();
    globals.this_MainActivity = this;
}

và trong một chuỗi, tôi mở hộp thoại như thế này.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Mở MainActivity
  2. Bắt đầu một chủ đề.
  3. Mở hộp thoại từ luồng -> làm việc.
  4. Nhấp vào "nút Quay lại" (onCreate sẽ được gọi và xóa MainActivity đầu tiên)
  5. MainActivity mới sẽ bắt đầu. (và lưu bản sao vào hình cầu)
  6. Mở hộp thoại từ luồng đầu tiên -> nó sẽ mở và hoạt động.

:)


4
Không bao giờ giữ một tham chiếu tĩnh cho một Hoạt động. Nó sẽ gây rò rỉ bộ nhớ
Leandroid
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.