从我的主 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();
}
//send to some other activity
。我想如果你评论你要去一个新活动的部分,这个错误就会消失。该错误似乎发生了,因为您之前的对话框被完全关闭,您的新活动开始了。在 onPostExecute()
中,您有警报对话框,并且您正在提供 login
活动的上下文。但是您正在导航到其他活动,因此上下文变得错误。因此,您收到此错误!请参阅 stackoverflow.com/questions/15104677/… 类似问题。
android.view.WindowManager$BadTokenException: Unable to add window"
问题 :
当应用程序尝试通过打开对话框从后台线程 (AsyncTask) 通知用户时,会发生此异常。如果您尝试从后台线程(通常从 AsyncTask 的 onPostExecute())修改 UI,并且如果活动进入完成阶段,即)显式调用完成(),用户按下主页或后退按钮或 Android 进行的活动清理,那么您得到这个错误。
原因 :
此异常的原因是,正如异常消息所述,活动已完成,但您正试图显示一个包含已完成活动上下文的对话框。由于没有用于显示 android 运行时的对话框的窗口,因此会引发此异常。
解决方案:
使用 Android 调用的 isFinishing() 方法来检查该活动是否处于完成过程中:无论是显式的 finish() 调用还是 Android 进行的活动清理。通过使用这种方法,很容易避免在活动完成时从后台线程打开对话框。还要为活动维护一个弱引用(而不是强引用,以便在不需要时可以销毁活动)并在使用此活动引用执行任何 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();
}
}
}
}
更新 :
窗口令牌:
顾名思义,窗口令牌是一种特殊类型的 Binder 令牌,窗口管理器使用它来唯一标识系统中的窗口。窗口令牌对于安全性很重要,因为它们使恶意应用程序无法在其他应用程序的窗口之上进行绘制。窗口管理器通过要求应用程序将其应用程序的窗口令牌作为添加或删除窗口的每个请求的一部分来防止这种情况。如果令牌不匹配,窗口管理器会拒绝请求并抛出 BadTokenException。如果没有窗口令牌,这个必要的识别步骤将无法进行,并且窗口管理器将无法保护自己免受恶意应用程序的侵害。
一个真实的场景:
当应用程序第一次启动时,ActivityManagerService 会创建一种特殊的窗口令牌,称为应用程序窗口令牌,它唯一标识应用程序的顶级容器窗口。活动管理器将此令牌同时提供给应用程序和窗口管理器,并且应用程序每次想在屏幕上添加新窗口时都将令牌发送给窗口管理器。这确保了应用程序和窗口管理器之间的安全交互(通过使其不可能在其他应用程序之上添加窗口),并且还使活动管理器可以轻松地直接向窗口管理器发出请求。
我有对话框显示功能:
void showDialog(){
new AlertDialog.Builder(MyActivity.this)
...
.show();
}
我收到此错误,我只需要在调用此对话框显示函数之前检查 isFinishing()
。
if(!isFinishing())
showDialog();
if(!MyActivity.this.isFinishing())
吗?如果在 MyActivity 中不正确
可能的原因是警报对话框的上下文。您可能已完成该活动,因此它试图在该上下文中打开但已关闭。尝试将该对话框的上下文更改为您的第一个活动,因为它直到最后才会完成。
例如
而不是这个。
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
尝试使用
AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
首先,您不能在不覆盖 doInBackground 的情况下扩展 AsyncTask
第二次尝试从构建器创建 AlterDailog,然后调用 show()。私人布尔可见=假;类 chkSubscription 扩展 AsyncTask
我在 onCreate
中创建 Dialog 并将其与 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();
我在没有关闭/关闭对话框的情况下完成了 Home Activity 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();
就我而言,我重构了代码并将对话框的创建放在一个单独的类中。我只交出了单击的视图,因为视图已经包含一个上下文对象。尽管都在 MainThread 上运行,但这导致了相同的错误消息。
然后我也切换到移交 Activity 并在对话框创建中使用它的上下文 -> 现在一切正常。
fun showDialogToDeletePhoto(baseActivity: BaseActivity, clickedParent: View, deletePhotoClickedListener: DeletePhotoClickedListener) {
val dialog = AlertDialog.Builder(baseActivity) // <-- here
.setTitle(baseActivity.getString(R.string.alert_delete_picture_dialog_title))
...
}
我,无法正确格式化代码片段,抱歉:(
我收到了这个错误,但我的错误来自 Toasts,而不是 Dialog。
我的布局中有活动和片段。 Toast 的代码在 Activity 类中。片段在活动之前加载。
我认为 Toast 代码是在 Context/Activity 完成初始化之前命中的。我认为是命令 Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();
中的 getApplicationContext()
尝试这个 :
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();
}
}
}
}
有了这个全局变量的想法,我将 MainActivity 实例保存在 onCreate(); Android global variable
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);
打开 MainActivity 启动一个线程。从线程打开对话框-> 工作。单击“返回按钮”(将调用 onCreate 并删除第一个 MainActivity)新的 MainActivity 将启动。 (并将其实例保存到全局变量)从第一个线程打开对话框->它将打开并工作。
:)