developer tip

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

copycodes 2020. 8. 14. 07:55
반응형

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


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

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


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

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

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

if(!isFinishing())
    showDialog();

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

예 :

이것보다.

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

사용하려고

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

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

에서 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();

나는 이것을 해결해 본다.

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

이 시도 :

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

}
}

with this globals variables idea, I saved MainActivity instance in onCreate(); Android global variable

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

and Open dialog like this. it worked.

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

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

and in a thread, I open dialog like this.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Open MainActivity
  2. Start a thread.
  3. Open dialog from thread -> work.
  4. Click "Back button" ( onCreate will be called and remove first MainActivity)
  5. New MainActivity will start. ( and save it's instance to globals )
  6. Open dialog from first thread --> it will open and work.

: )

참고URL : https://stackoverflow.com/questions/18662239/android-view-windowmanagerbadtokenexception-unable-to-add-window-on-buider-s

반응형