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

gym*_*hoe 652 android builder android-context android-alertdialog

我的Activity正在尝试创建一个AlertDialog,它需要一个Context作为参数.如果我使用,这可以按预期工作:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
Run Code Online (Sandbox Code Playgroud)

但是,我很谨慎地使用"this"作为上下文,因为当Activity被销毁并重新创建甚至在屏幕旋转之类的简单事件时,可能会发生内存泄漏.来自Android开发者博客上的相关帖子:

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

但是对于这AlertDialog()两者来说,getApplicationContext()或者getApplication()作为一个Context是可接受的,因为它抛出异常:

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

每引用:1,2,3,等等.

那么,这真的应该被视为一个"错误",因为我们被正式建议使用Activity.getApplication(),但它没有宣传的功能吗?

吉姆

Ste*_*n L 1324

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

  • 大!只是为了评论...你有时可能需要在全局存储"this"(例如),以便在监听器实现的方法中访问它,并且拥有它自己的"this".在这种情况下,您将全局定义"上下文上下文",然后在onCreate中设置"context = this",然后引用"上下文".希望它也派上用场. (65认同)
  • @StevenL为了做你所说的,你应该使用ExternalClassName.this来明确地引用外部类的"this". (27认同)
  • 如果您的对话用于回调并且在调用回调之前离开活动,那么不会使用"this"泄漏吗?至少这是Android似乎在logcat中抱怨的东西. (11认同)
  • 实际上,由于`Listener`类通常是匿名内部的,我倾向于只做`最后的上下文ctx = this;`而我离开了;) (7认同)
  • 我不建议@StevenLs方法,因为你可以很容易地泄漏该活动的内存,除非你记得清除onDestroy中的静态引用 - Artem是正确的.StevenLs的方法是缺乏对Java工作原理的理解 (6认同)
  • 通过上面的评论,我指的是上面的@StevenL评论,其中包含"_sometimes需要存储`this`global_".在`static`字段中存储一个`Activity`上下文(在这种情况下是'this`指的是)是__big__ no-no,因为它很容易泄漏内存."@ ExternalClassName.this`"的@Artems方法绝对是正确的.`getApplicationContext()`可能会因为需要将对话框附加到`Activity`上下文而不是`Application`上下文而给出错误... (3认同)
  • ...当涉及到`Context`类时,Android有一点含糊之处,特别是因为`Activity`和`Application`都扩展了上下文但是具有不同的外部状态,并且在所有情况下并且真正可以互相替换,哪种排序反对__ [SOLID]的__L__部分(http://en.wikipedia.org/wiki/SOLID_(object-oriented_design))__ OOP设计原则 - 因此导致混淆,如OPs问题. (2认同)
  • 这不是问题的答案.问题是:为什么AlertDialog.Build(上下文上下文)仅在您将Activity作为上下文传递时才起作用?或者,问另一种方式:当您传递非活动的上下文时,为什么AlertDialog.Build(上下文上下文)不起作用? (2认同)

Tru*_*oke 189

使用this对我不起作用,但MyActivityName.this确实如此.希望这可以帮助任何无法this上班的人.

  • 当你从内部类中使用`this`时会发生这种情况.如果要引用外部类的实例,则必须像使用`OuterClass.this`那样指定.只使用`this`总是引用最内层的实例. (63认同)

cod*_*zjx 56

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

将以下权限添加到清单:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Run Code Online (Sandbox Code Playgroud)

  • 您似乎无法在API 23中启用此权限https://code.google.com/p/android-developer-preview/issues/detail?id=3123 (3认同)
  • 当您在服务中显示进度对话框时,这非常有用 (2认同)

ONE*_*ONE 35

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

要创建对话框,您需要活动上下文服务上下文,而不是应用程序上下文(getApplicationContext()和getApplication()都返回应用程序上下文).

以下是您获取活动背景的方式:

(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"(或其他引用)无限引用的逻辑错误.记得检查链引用.以下是一些可用于帮助您分析内存使用并找到那些讨厌的内存泄漏的工具:


Kev*_*oil 34

您的对话框不应该是"需要上下文的长期对象".文档令人困惑.基本上如果你做的事情如下:

static Dialog sDialog;
Run Code Online (Sandbox Code Playgroud)

(注意静态)

然后在你做过的某个活动中

 sDialog = new Dialog(this);
Run Code Online (Sandbox Code Playgroud)

您可能会在轮换期间泄漏原始活动或类似会破坏活动.(除非你在onDestroy中清理,但在这种情况下你可能不会使Dialog对象静态)

对于某些数据结构,将它们设置为静态并基于应用程序的上下文是有意义的,但通常不适用于与UI相关的事物,如对话框.所以像这样:

Dialog mDialog;

...

mDialog = new Dialog(this);
Run Code Online (Sandbox Code Playgroud)

很好,不应该泄漏活动,因为mDialog将被活动释放,因为它不是静态的.


Gru*_*rux 24

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

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

  • 这也适用于我,我将它传递给我正在使用的外部AsyncTask的构造函数(它显示了一个进度对话框). (4认同)
  • 这是更复杂任务的真实答案:) (3认同)

Mah*_*man 23

Activity中使用:

MyActivity.this
Run Code Online (Sandbox Code Playgroud)

片段中:

getActivity();
Run Code Online (Sandbox Code Playgroud)


小智 20

Activity按钮的点击出一个对话框

Dialog dialog = new Dialog(MyActivity.this);
Run Code Online (Sandbox Code Playgroud)

为我工作.


Mik*_*nin 18

小黑客:你可以防止GC破坏你的活动(你不应该这样做,但它可以在某些情况下帮助.不要忘记设置contextForDialognull不再需要时):

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();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)


Mil*_*oid 16

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

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


Muh*_*zan 14

只需使用以下内容:

对于 Java 用户

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

或者

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

如果您正在使用片段 --> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

对于 Kotlin 用户

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

或者

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

如果您正在使用片段 --> val builder = AlertDialog.Builder(activity!!)


mua*_*aaz 13

如果您正在使用片段并使用AlertDialog/Toast消息,请在context参数中使用getActivity().

像这样

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();
Run Code Online (Sandbox Code Playgroud)


小智 9

加入

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
Run Code Online (Sandbox Code Playgroud)

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

它现在对我有用.甚至关闭并打开应用程序后,当时给了我错误.


小智 9

ProgressDialog在一个片段中使用并且在getActivity().getApplicationContext()作为构造函数参数传递时遇到此错误.改变它getActivity().getBaseContext()也不起作用.

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

progressDialog = new ProgressDialog(getActivity());


MSA*_*MSA 6

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


小智 6

如果您不在Activity之外,那么您需要在函数"NameOfMyActivity.this"中使用Activity作为活动,例如:

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);
Run Code Online (Sandbox Code Playgroud)


cur*_*gie 5

如果您正在使用片段并使用AlertDialog / Toast消息,请getActivity()在context参数中使用。

为我工作。

干杯!


Eng*_*URK 5

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

例如,如果您将TabActivity作为具有两个选项卡的主机,并且每个选项卡都是另一个活动,并且如果您尝试从其中一个选项卡(活动)创建对话框,并且使用“ this”,那么您将获得异常,案例对话框应连接到承载所有活动且可见的主机活动。(您可以说大多数可见的父活动的上下文)

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


Ден*_*еня 5

对于未来的读者,这应该有所帮助:

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