buider.show ()의 "android.view.WindowManager $ BadTokenException : 창을 추가 할 수 없습니다."


118

내 메인 activity에서 내부 클래스를 호출하고 클래스 내의 메서드에서 AlertDialog. 해제 후 OK 버튼을 누르면 구매를 위해 Google Play로 이동합니다.

대부분의 경우 완벽하게 작동하지만 일부 사용자의 경우 충돌이 발생 builder.show()하고 "android.view.WindowManager$BadTokenException:충돌 로그에서 Unable to add window "를 볼 수 있습니다. 제안 해주세요.

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

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

또한 다른 경고에 전달하지 않는 다른 경고에서도 오류가 발생했습니다 activity. 다음과 같이 간단합니다.

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
이것이 완전한 코드라면 AsyncTask가 정말로 필요합니까?
Shobhit Puri 2013 년

이것은 완전한 코드가 아닙니다. 이것은 상당히 큰 코드이므로 여기에 충돌 보고서에서 문제가 발생한 부분 만 추가했습니다
MSIslam

좋아 좋아. 일반적으로 함수 이름과 주석을 게시 할 수 있습니다 (지금처럼). 이해하기 쉽습니다. :).
Shobhit Puri 2013 년

이 활동에서 다른 활동으로 이동하고 있습니까?
Shobhit Puri 2013 년

1
당신은 댓글을 작성했습니다 //send to some other activity. 새로운 활동에 갈 부분을 언급하면이 오류가 사라질 것이라고 생각합니다. 이전 대화 상자가 완전히 닫히고 새 활동이 시작되기 때문에 오류가 발생한 것 같습니다. 에는 onPostExecute()경고 대화 상자가 있으며 login활동 의 컨텍스트를 제공합니다 . 그러나 다른 활동으로 이동하고 있으므로 컨텍스트가 잘못됩니다. 따라서이 오류가 발생합니다! stackoverflow.com/questions/15104677/… 유사한 질문을 참조하십시오 .
Shobhit Puri 2013 년

답변:


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

문제 :

이 예외는 앱이 대화 상자를 열어 백그라운드 스레드 (AsyncTask)에서 사용자에게 알리려고 할 때 발생합니다.

백그라운드 스레드 (일반적으로 AsyncTask의 onPostExecute ()에서)에서 UI를 수정하려고하고 활동이 마무리 단계에 들어간 경우 (예 : 명시 적으로 finish ()를 호출하면 사용자가 홈 또는 뒤로 버튼을 누르거나 Android에서 만든 활동 정리) 이 오류가 발생합니다.

이유 :

이 예외의 이유는 예외 메시지에서 알 수 있듯이 활동이 완료되었지만 완료된 활동의 컨텍스트가있는 대화 상자를 표시하려고하기 때문입니다. Android 런타임을 표시 할 대화 상자가 없기 때문에이 예외가 발생합니다.

해결책:

isFinishing()Android에서 호출하는 메서드를 사용 하여이 활동이 완료 중인지 확인합니다. 명시적인 finish () 호출인지 Android에서 만든 활동 정리인지 확인합니다. 이 방법을 사용하면 활동이 끝날 때 백그라운드 스레드에서 대화 상자를 여는 것을 피하는 것이 매우 쉽습니다.

또한 weak reference활동에 대한를 유지하고 (활동이 필요하지 않은 경우 삭제 될 수 있도록 강력한 참조가 아님)이 활동 참조를 사용하여 UI를 수행하기 전에 (예 : 대화 상자 표시) 활동이 완료되지 않았는지 확인하십시오.

예 : .

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

업데이트 :

창 토큰 :

이름에서 알 수 있듯이 창 토큰은 창 관리자가 시스템에서 창을 고유하게 식별하는 데 사용하는 특수 유형의 바인더 토큰입니다. 창 토큰은 악의적 인 응용 프로그램이 다른 응용 프로그램의 창 위에 그릴 수 없도록하기 때문에 보안에 중요합니다. 창 관리자는 창을 추가하거나 제거하기위한 각 요청의 일부로 응용 프로그램이 응용 프로그램의 창 토큰을 전달하도록 요구하여이를 방지합니다. 토큰이 일치하지 않으면 창 관리자는 요청을 거부하고 BadTokenException을 발생시킵니다 . 창 토큰이 없으면이 필요한 식별 단계가 불가능하고 창 관리자는 악성 응용 프로그램으로부터 자신을 보호 할 수 없습니다.

 실제 시나리오 :

애플리케이션이 처음 시작될 때  ActivityManagerService  는 애플리케이션의 최상위 컨테이너 창을 고유하게 식별하는 애플리케이션 창 토큰이라는 특수한 종류의 창 토큰을 만듭니다. 활동 관리자는이 토큰을 응용 프로그램과 창 관리자 모두에게 제공하고 응용 프로그램은 화면에 새 창을 추가 할 때마다 토큰을 창 관리자에게 보냅니다. 이렇게하면 애플리케이션과 창 관리자 간의 안전한 상호 작용이 보장되고 (다른 애플리케이션 위에 창을 추가 할 수 없게함으로써) 활동 관리자가 창 관리자에게 직접 요청을 쉽게 할 수 있습니다.


이것은 많은 의미가 있습니다! 또한 귀하의 솔루션은 나에게 훌륭하게 들립니다. (y)
MSIslam 2013 년

'빈 마지막 필드 loginActivityWeakRef가 초기화되지 않았을 수 있습니다'메시지가 표시되고 다음과 같이 시도되었습니다. private final WeakReference <login> loginActivityWeakRef = new WeakReference <login> (login.this); 옳은
일인지

또한 생성자에서 오류를 표시했기 때문에 WeakReference <login> loginActivityWeakRef 전에 마지막을 제거했습니다.
MSIslam 2013 년

1
new chkCubscription (this) .execute ( ""); 새로운 chkCubscription.execute ( "") 대신에); 위에 게시 한대로.
Ritesh Gune 2013 년

2
끔찍한 오류 !! 튜토리얼을 따르고 있으며 @PhilRoggenbuck으로 StartActivity (...)를 호출하기 직전에 Toast..Show ()를 호출하여 문제가 발생했습니다. 이를 해결하기 위해 새로 호출 된 활동에서 토스트를 대신 이동했습니다!
Thierry

26

기능을 보여주는 대화 상자가 있습니다.

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

이 오류가 발생 isFinishing()하고이 대화 상자 표시 기능을 호출하기 전에 확인 해야했습니다.

if(!isFinishing())
    showDialog();

1
우리가 써야하지 if(!MyActivity.this.isFinishing())않나요? MyActivity에서 올바르지 않은 경우
Bibaswann Bandyopadhyay

2
Android가 이미 완료된 코드를 실행하는 이유는 무엇입니까? 이 솔루션을 따른다면 비슷한 문제를 피하기 위해 isFinishing을 얼마나 많이 사용해야하는지 상상해보십시오.
David

@David 나는 이것이 백그라운드 스레드에서 호출되는 대화와 같은 몇 가지 세부 사항이 누락되었다고 생각하지만 지금은 귀하의 요점에 완전히 동의합니다.
버려진 카트

좋은 점, 왜 내가 isFinishing을 확인해야할까요!
Chibueze Opata

9

가능한 이유는 경고 대화 상자의 컨텍스트입니다. 해당 활동을 완료하여 해당 컨텍스트에서 열려고하지만 이미 닫혀있을 수 있습니다. 끝까지 완료되지 않으므로 해당 대화의 컨텍스트를 첫 번째 활동으로 변경하십시오.

예 :

이것보다.

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

사용하려고

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

3
  • 먼저 doInBackground를 재정의하지 않고 AsyncTask를 확장 할 수 없습니다.
  • 두 번째로 빌더에서 AlterDailog를 생성 한 다음 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
답변 해주셔서 감사합니다. 실제로 doInBackground 메서드를 사용했지만 경고와 관련이 없기 때문에 여기서는 언급하지 않았습니다. builder.create () 추가와 관련하여 잘 작동하는 것처럼 보이지만 모든 사람에게 잘 작동하는지 알 수 없습니다. 앞서 말했듯이 현재 코드도 잘 작동하지만 소수의 사용자에게는 몇 번만 창을 추가 할 수 없음 문제가 표시됩니다. 내 코딩에서 실제 문제가 무엇인지 제안 해 주시겠습니까?
MSIslam 2013 년

이 경우 사용자는 onPostExecute가 호출되기 전에 활동을 종료하므로 대화 상자를 보유 할 창이 없으며 이로 인해 애플리케이션이 충돌합니다. onStop에 플래그를 추가하여 활동이 더 이상 보이지 않는지 확인한 다음 대화 상자를 표시하지 마십시오.
moh.sukhni 2013 년

doInBackground ()의 웹 서비스 호출 결과를 기반으로 사용자가 구독하지 않았는지 확인할 때 builder.show ()가 조건하에 있기 때문에 onPostExecute가 실제로 호출됩니다. 따라서 onPostExecute가 호출되지 않았다면 builder.show () 줄에 도달하지 않았을 것입니다.
MSIslam 2013 년

onPostExecute는 doInBackground 이후에 기본적으로 호출되며, 호출 할 수 없으며 실행될 내용이 있습니다.
moh.sukhni 2013 년

1
비동기 작업은 사용자가 활동에서 탐색 한 후에도 계속 작동하며 UI를 처리 할 활동이 없기 때문에 builder.show ()가 애플리케이션을 망칠 수 있습니다. 따라서 앱이 웹에서 데이터를 가져 오지만 데이터를 가져 오기 전에 활동이 파괴되었습니다.
moh.sukhni 2013 년

1

에서 Dialog를 만들고 및와 onCreate함께 사용 show하고 hide있습니다. 나에게 근본 원인은 활동 onBackPressed을 마치고있는을 해제 하지 않았습니다 Home.

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

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) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

0

나는 이것을 해결해 본다.

 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

이 시도 :

    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

이 전역 변수 아이디어로 MainActivity 인스턴스를 onCreate (); Android 전역 변수

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

다음과 같이 대화 상자를 엽니 다. 작동했습니다.

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

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

스레드에서 다음과 같은 대화 상자를 엽니 다.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. MainActivity 열기
  2. 스레드를 시작하십시오.
  3. 스레드에서 대화 열기-> 작업.
  4. "뒤로 버튼"을 클릭합니다 (onCreate가 호출되고 첫 번째 MainActivity가 제거됨).
  5. 새 MainActivity가 시작됩니다. (그리고 인스턴스를 전역에 저장하십시오)
  6. 첫 번째 스레드에서 대화 열기-> 열리고 작동합니다.

:)


4
활동에 대한 정적 참조를 유지하지 마십시오. 그것은 메모리 누수를 일으킬 것입니다
Leandroid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.