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


665

我的活动试图创建一个AlertContext,它需要一个Context作为参数。如果我使用,这可以按预期工作:

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

但是,我很乐意使用“ this”作为上下文,因为即使在诸如屏幕旋转之类的简单操作中销毁并重新创建Activity时,也可能会发生内存泄漏。摘自Android开发者博客上的相关文章

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

但是对于AlertDialog()都不是getApplicationContext()getApplication()不能作为上下文接受的情况,因为它引发了异常:

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

每引用:123,等等。

那么,这是真的应该被视为“错误” Activity.getApplication()吗,因为我们被正式建议使用,但它不能像宣传的那样起作用?

吉姆


对于其中R.Guy建议使用getApplication的第一项参考: android-developers.blogspot.com/2009/01/...
gymshoe




Answers:


1354

而是getApplicationContext()使用ActivityName.this


67
大!只是对此发表评论。您有时可能需要全局存储“ this”(例如),以便在拥有其“ this”的侦听器的已实现方法中访问它。在这种情况下,您将全局定义“上下文上下文”,然后在onCreate中设置“ context = this”,然后引用“ context”。希望也能派上用场。
史蒂文·L

8
实际上,由于Listener课程通常是匿名的,所以我倾向于这样做,final Context ctx = this;而我却不在了;)
Alex

28
@StevenL为了执行您所说的,您应该使用ExternalClassName.this显式引用外部类的“ this”。
Artem Russakovskii 2011年

11
如果您的对话框用于回调中并且您在调用回调之前离开活动,是否会使用“ this”泄漏它?至少这就是Android在logcat中抱怨的地方。
Artem Russakovskii 2011年

6
我不建议使用@StevenLs方法,因为您很容易泄漏该活动的内存,除非您记得清除onDestroy中的静态引用-Artem是正确的。StevenLs的方法源于对Java工作原理的缺乏了解
Dori

191

使用this对我不起作用,但对我MyActivityName.this有用。希望这对无法this上班的人有所帮助。


63
当您this从内部类内部使用时,就会发生这种情况。如果要引用外部类的实例,则必须像使用那样指定OuterClass.this。仅使用this始终引用最内部类的实例即可。
卡卡2012年

60

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

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

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

1
我得到无法添加窗口android.view.ViewRootImpl$W@426ce670 -拒绝使用此窗口权限类型
拉姆G.

添加权限:<uses-permission android:name =“ android.permission.SYSTEM_ALERT_WINDOW” />
codezjx 2015年

3
似乎您无法在API 23及更高版本中启用此权限code.google.com/p/android-developer-preview/issues/…–
roy zhang

1
您可以将其用于API 23及更高版本,但是需要提示用户:startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse(“ package:” + getPackageName())),OVERLAY_PERMISSION_REQ_CODE); 但是,是否应该使用是另一回事...
Ben Neill

2
当您在服务内部显示进度对话框时,这很有用
Anand Savjani

37

当您说“ ...”时,您已经正确地识别了问题。对于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不会释放该内存。

为防止内存泄漏,请检查代码中是否存在逻辑错误,这些逻辑错误会导致分配的内存被“此”(或其他引用)无限期地引用。切记还要检查链引用。以下是一些可用于帮助您分析内存使用情况并查找那些讨厌的内存泄漏的工具:


对于活动,您还可以使用ActivityName.this,其中ActivityName(显然)是您活动的名称(例如MainActivity)
Luis Cabrera Benito

34

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

static Dialog sDialog;

(注意静态

然后在某处的活动中

 sDialog = new Dialog(this);

您很可能在轮换期间泄漏了原始活动,或者可能破坏活动的类似活动。(除非您在onDestroy中进行清理,但是在那种情况下,您可能不会使Dialog对象成为静态对象)

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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


我从asynctask调用它,这对我有用,伴侣
MemLeak 2012年

我的对话框是静态的,一旦我删除了静态声明,它就起作用了。
Ceddy Muhoza '16

25

我必须通过片段中显示的自定义适配器上的构造函数发送上下文,并且getApplicationContext()出现此问题。我解决了:

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


4
这也对我有用,我将其传递给我正在使用的外部AsyncTask的构造函数(它显示进度对话框)。
Rohan Kandwal 2014年

3
这是对更复杂任务的真正答案:)
teejay 2014年

1
我同意@teejay
Erdiİzgi2015年


20

Activity单击按钮时显示一个对话框

Dialog dialog = new Dialog(MyActivity.this);

为我工作。


19

***** Kotlin版本*****

您应通过this@YourActivity而不是applicationContextbaseContext


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();
    }
    ...
}

@MurtuzaKabul之所以起作用,是因为此== PostActivity继承自Activity->继承自Context,因此当您通过对话框传递上下文时,您实际上是在传递活动
Elad Gelman 2013年

13

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

像这样

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

12

只需使用以下内容:

对于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!!)


9

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

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

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


9

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

对我有效的解决办法是通过getActivity(); 即

progressDialog = new ProgressDialog(getActivity());


6

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


6

如果您不在活动范围内,则需要在函数“ 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);

5

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

为我工作。

干杯!


5

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

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

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


4

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

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


2

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

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

2

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

runOnUiThread()在这种情况下使用。



1

看完API之后,您可以将活动的对话框或getActivity传递给对话框(如果您位于一个片段中),然后在return方法中使用dialog.dismiss()强制将其清除,以防止泄漏。

尽管我所知道的任何地方都没有明确说明,但似乎您只是为了执行此操作而将您传递回OnClickHandlers中的对话框。


0

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

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

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

在适配器上接收:

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

现在您可以在Builder上使用

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

-1

这是我为应用程序解决的相同错误的方法:
在创建对话框后添加以下行:

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

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

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

大卫


-1
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getWindow().getDecorView().getRootView().getContext());

builder.setTitle("Confirm");
builder.setMessage("Are you sure you want delete your old account?");

builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
        //Do nothing but close the dialog



        dialog.dismiss();

    }
});

builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

        //Do nothing
        dialog.dismiss();
    }
});

android.support.v7.app.AlertDialog alert = builder.create();
alert.show();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.