将getApplication()作为上下文抛出“无法添加窗口-令牌null不适用于应用程序”的对话框

我的活动正在尝试创建一个需要上下文作为参数的警报对话框。如果我使用:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

然而,我对使用“this”作为上下文持谨慎态度,因为即使在屏幕旋转等简单的过程中,当活动被销毁和重新创建时,也有可能发生内存泄漏。从Android开发者博客上的相关文章

有两种简单的方法可以避免上下文相关的内存泄漏。最明显的一种是避免转义上下文超出其自身范围。上面的例子展示了静态引用的情况,但内部类及其对外部类的隐式引用可能同样危险。第二个解决方案是使用Application上下文。只要你的应用程序还活着,这个上下文就会存在,并且不依赖于活动的生命周期。如果你打算保留需要上下文的长生命周期对象,请记住应用程序对象。你可以通过调用Context.getApplication ationContext()或Activity.getApplication()轻松获取它。

但是对于AlertDialog()getApplicationContext()getApplication()都不能作为Context接受,因为它抛出了异常:

“无法添加窗口-令牌null不适用于应用程序”

参考文献:123等。

那么,这真的应该被认为是一个“bug”,因为我们被正式建议使用Activity.getApplication(),但它并没有像宣传的那样起作用吗?

吉姆

410654 次浏览

您的对话框不应该是“需要上下文的长期对象”。留档令人困惑。基本上,如果您执行以下操作:

static Dialog sDialog;

(注意静态

然后在某处你做的活动中

 sDialog = new Dialog(this);

你可能会在旋转或类似的过程中泄漏原始活动,从而破坏该活动。(除非你在onDestroy中清理,但在这种情况下,你可能不会使Dialog对象静态)

对于某些数据结构,使它们静态并基于应用程序的上下文是有意义的,但通常不适用于与UI相关的东西,例如对话框。所以像这样的东西:

Dialog mDialog;


...


mDialog = new Dialog(this);

很好,不应该泄漏活动,因为mDialog将与活动一起释放,因为它不是静态的。

而不是getApplicationContext(),只需使用ActivityName.this

使用this对我不起作用,但是MyActivityName.this起作用了。希望这能帮助那些无法使用this的人。

在我的案例工作中:

this.getContext();

小技巧:您可以防止GC破坏您的活动(您不应该这样做,但它可以在某些情况下提供帮助。当不再需要时,不要忘记将contextForDialog设置为null):

public class PostActivity extends Activity  {
...
private Context contextForDialog = null;
...
public void onCreate(Bundle savedInstanceState) {
...
contextForDialog = this;
}
...
private void showAnimatedDialog() {
mSpinner = new Dialog(contextForDialog);
mSpinner.setContentView(new MySpinner(contextForDialog));
mSpinner.show();
}
...
}

如果您使用片段并使用AlertDialog / Toast消息,请在上下文参数中使用getActivity()

为我工作。

干杯!

在上下文的参数位置尝试getParent(),例如新的AlertDialog.Builder(getParent());希望它会起作用,它对我有用。

Activity中点击显示对话框的按钮

Dialog dialog = new Dialog(MyActivity.this);

为我工作。

我认为如果您尝试从不是主UI线程的线程显示对话框,也可能会发生这种情况。

在这种情况下使用runOnUiThread()

使用MyDialog md = new MyDialog(MyActivity.this.getParent());

尝试使用将在对话框下的活动的上下文。但是当你使用“this”关键字时要小心,因为它不会每次都起作用。

例如,如果你有两个选项卡作为主机,每个选项卡是另一个活动,如果你尝试从一个选项卡(活动)创建对话框,如果你使用"this",那么你会得到异常, 在这种情况下,对话框应该连接到托管所有内容且可见的主机活动。(你可以说最可见的父活动的上下文)

我没有从任何文档中找到此信息,而是通过尝试。这是我没有强大背景的解决方案,如果有人有更好的知识,请随时发表评论。

如果您正在使用片段并使用AlertDialog/Toast消息,请在上下文参数中使用getActive()。

像这样

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

如果你在活动之外,那么你需要在函数中使用“NameOfMyActivity.this”作为活动活动,例如:

public static void showDialog(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage("Your Message")
.setPositiveButton("Yes", dialogClickListener)
.setNegativeButton("No", dialogClickListener).show();
}




//Outside your Activity
showDialog(NameOfMyActivity.this);

我必须通过片段中显示的自定义适配器上的构造函数发送我的上下文,并且在getApplication ationContext()中遇到了这个问题。我使用:

片段中的this.getActivity().getWindow().getContext()onCreate回调。

查看API后,您可以将对话框传递给您的活动,如果您在片段中,则传递给getActivity,然后在返回方法中使用dialog.dismiss()强制清理它以防止泄漏。

虽然在我知道的任何地方都没有明确说明,但似乎你只是为了做到这一点而在OnClickHandler中传回对话框。

您可以继续使用getApplicationContext(),但在使用之前,您应该添加此标志:dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),错误将不显示。

将以下权限添加到您的清单中:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

添加

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

清单中的"android.permission.SYSTEM_ALERT_WINDOW"/>

它现在为我工作。甚至关闭和打开应用程序后,当时给我的错误。

或者另一种可能性是创建Dialog,如下所示:

final Dialog dialog = new Dialog(new ContextThemeWrapper(
this, R.style.MyThemeDialog));

对于未来的读者,这将有助于:

public void show() {
if(mContext instanceof Activity) {
Activity activity = (Activity) mContext;
if (!activity.isFinishing() && !activity.isDestroyed()) {
dialog.show();
}
}
}

当您说“…对于AlertDialog(),getApplication ationContext()或getApplication()都不能作为Context接受时,您已经正确地识别了问题,因为它抛出了异常:'无法添加窗口-令牌null不适用于应用程序'"

要创建Dialog,您需要活动背景服务上下文,而不是应用环境(getApplication ationContext()和getApplication()都返回一个Application Context)。

以下是如何获得活动背景

(1)在活动或服务中:

AlertDialog.Builder builder = new AlertDialog.Builder(this);

(2)在片段中: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

内存泄漏不是this引用固有的问题,this引用是对象对自身的引用(即对用于存储对象数据的实际分配内存的引用)。它发生在任何分配的内存中,垃圾收集器(GC)在分配的内存超过其有用寿命后无法释放这些内存。

大多数情况下,当变量超出作用域时,内存会被GC回收。然而,当变量持有的对对象的引用,例如“x”,即使在对象已经超过其有用寿命后仍然存在,也会发生内存泄漏。因此,只要“x”持有对它的引用,分配的内存就会丢失,因为GC不会在该内存仍被引用的时间内释放了内存。有时,内存泄漏并不明显,因为一连串的参考到分配的内存。在这种情况下,GC不会释放内存,直到对该内存的所有引用都被删除。

为了防止内存泄漏,请检查代码中是否存在导致已分配内存被this(或其他引用)无限期引用的逻辑错误。记住也要检查链引用。这里有一些工具可以帮助你分析内存使用并找到那些讨厌的内存泄漏:

我在片段中使用ProgressDialog,并在传递getActivity().getApplicationContext()作为构造函数参数时遇到此错误。将其更改为getActivity().getBaseContext()也不起作用。

对我有效的解决方案是通过getActivity();即。

progressDialog = new ProgressDialog(getActivity());

活动中只需使用:

MyActivity.this

片段:

getActivity();

*****kotlin版本*****

您应该通过this@YourActivity而不是applicationContextbaseContext

以下是我如何解决我的应用程序的相同错误:
创建对话框后添加以下行:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);

您不需要获取上下文。如果您在当前弹出的对话框上弹出另一个对话框,或者不方便获取上下文,这将特别有用。

希望这可以帮助您进行应用程序开发。

大卫

如果您的对话框正在适配器上创建:

将活动传递给适配器构造函数:

adapter = new MyAdapter(getActivity(),data);

在适配器上接收:

 public MyAdapter(Activity activity, List<Data> dataList){
this.activity = activity;
}

现在您可以在Builder上使用

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);

只需使用以下:

对于Java用户

如果您正在使用活动-->AlertDialog.Builder builder = new AlertDialog.Builder(this);

AlertDialog.Builder builder = new AlertDialog.Builder(your_activity.this);

如果您使用的是Frament-->AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

对于KOTLIN用户

如果您正在使用活动-->val builder = AlertDialog.Builder(this)

val builder = AlertDialog.Builder(this@your_activity.this)

如果您使用的是Frament-->val builder = AlertDialog.Builder(activity!!)

伙计们,我有一个简单的备忘单。 创建一个文件,给它任何名称,然后在其中写入以下代码

fun Context.alertdialog(context: Context, msg: String, icon: Drawable, title:String){
val alertDialog = AlertDialog.Builder(context)
alertDialog.setIcon(icon)
.setTitle(title)
.setMessage(msg)
alertDialog.show()
}

现在,当您需要显示警报对话框时,只需在任何地方使用此方法

requireActivity().alertdialog(requireContext(), resources.getString(R.string.pass_title),
resources.getDrawable(R.drawable.pass_ic_name), "title")

祝你好运 静态编程语言

对我有用的是传递活动而不是上下文。

我想为我的对话框定制一个布局,但是为了保持我的代码独立,我在一个单独的类中创建了它,否则我将不得不将该代码块复制并粘贴到我想要使用对话框的每个活动中。

解决方案解释了我的情况,但它给出了核心解决方案:

  1. 当我使用ViewAdapter时,我使用活动(不是上下文ex.->[kotlin]活动:活动)作为参数->ex.[kotlin]this@MainActivity初始化适配器
  2. 然后我把这个参数传给了观众
  3. 然后将其再次传递给将使Dialog膨胀的类。

使用活动[可选名称]:活动[强制类型]在任何地方,直到它到达你想要膨胀的对话框

它有很多传递,但它确实比在任何地方复制和粘贴相同的代码更有意义