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);
- Open MainActivity
- Start a thread.
- Open dialog from thread -> work.
- Click "Back button" ( onCreate will be called and remove first MainActivity)
- New MainActivity will start. ( and save it's instance to globals )
- Open dialog from first thread --> it will open and work.
: )
'developer tip' 카테고리의 다른 글
IntelliJ를 사용하여 클래스 다이어그램 생성 (0) | 2020.08.14 |
---|---|
: after를 사용하여 부동 요소 지우기 (0) | 2020.08.14 |
JPA 또는 JDBC는 어떻게 다릅니 까? (0) | 2020.08.14 |
각도 앱에 Blob (.pdf) 표시 (0) | 2020.08.14 |
contenteditable, 텍스트 끝에 캐럿 설정 (브라우저 간) (0) | 2020.08.14 |