メインの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ランタイムを表示するウィンドウがないため、この例外がスローされます。
解決策:
Androidによって呼び出される
isFinishing()
メソッドを使用して、このアクティビティが終了処理中であるかどうかを確認します。明示的な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();
次に、ビルダーから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();
}
これを試して :
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();
}
}
}
}
これを解決してみました。
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();
onCreate
name__でダイアログを作成し、show
name__およびhide
name__と共に使用しています。私にとって、根本原因はonBackPressed
name__を終了していなかったため、Home
name__アクティビティが終了していました。
@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
name__を終了していました。
ダイアログを閉じると、クラッシュは消えました。
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();