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

时间:2011-04-26 21:21:07

标签: android android-alertdialog android-context builder

My Activity正在尝试创建一个需要Context作为参数的AlertDialog。如果我使用:

,这可以按预期工作
AlertDialog.Builder builder = new AlertDialog.Builder(this);

但是,我很谨慎地使用“this”作为上下文,因为当Activity被销毁并重新创建甚至在屏幕旋转之类的简单事件时,可能会发生内存泄漏。来自related post on the Android developer's blog

  

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

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

  

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

每个参考文献:123等。

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

吉姆

29 个答案:

答案 0 :(得分:1292)

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

答案 1 :(得分:183)

使用this对我不起作用,但MyActivityName.this做了。希望这可以帮助任何无法让this工作的人。

答案 2 :(得分:55)

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

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

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

答案 3 :(得分:34)

您的对话框不应该是“需要上下文的长期对象”。文档令人困惑。基本上,如果你做了类似的事情:

static Dialog sDialog;

(请注意静态

然后在你做某个地方的活动中

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

答案 4 :(得分:32)

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

答案 5 :(得分:23)

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

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

onCreate

答案 6 :(得分:21)

活动中的

只需使用:

MyActivity.this

片段:

getActivity();

答案 7 :(得分:19)

点击显示对话框的按钮<{1}}

Activity

为我工作。

答案 8 :(得分:18)

小黑客:你可以防止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();
    }
    ...
}

答案 9 :(得分:13)

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

像这样

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

答案 10 :(得分:12)

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

您应该传递this@YourActivity而不是applicationContextbaseContext

答案 11 :(得分:9)

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

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

progressDialog = new ProgressDialog(getActivity());

答案 12 :(得分:9)

添加

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

清单

中的

"android.permission.SYSTEM_ALERT_WINDOW"/>

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

答案 13 :(得分:6)

只需使用以下内容:

面向JAVA用户

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

OR

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

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

供KOTLIN用户使用

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

OR

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

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

答案 14 :(得分:6)

如果您不在活动之外,那么您需要在您的函数“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);

答案 15 :(得分:6)

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

答案 16 :(得分:5)

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

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

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

答案 17 :(得分:5)

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

为我工作。

干杯!

答案 18 :(得分:4)

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

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

答案 19 :(得分:2)

或者另一种可能性是按如下方式创建Dialog:

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

答案 20 :(得分:2)

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

在这种情况下使用runOnUiThread()

答案 21 :(得分:2)

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

答案 22 :(得分:2)

在我的案例中:

this.getContext();

答案 23 :(得分:1)

伙计们,我得到了一个简单的备忘单。 创建一个文件,为其命名,然后在其中编写此代码

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")

好运 为科特林

答案 24 :(得分:1)

对我有用的是通过活动而不是上下文。

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

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

  1. 当我使用ViewAdapter时,我以Activity(不是上下文,例如-> [kotlin] activity:Activity)作为参数-> ex初始化了适配器。 [kotlin] this @ MainActivity
  2. 然后我将该参数传递给了Viewholder
  3. 然后将其再次传递给将使Dialog膨胀的类。

在所有要使用的对话框上使用活动[可选名称]:活动[强制类型]

传递很多,但是在所有地方复制和粘贴相同代码确实更有道理

答案 25 :(得分:1)

在查看API之后,如果你在一个片段中,你可以传递你的activity或getActivity对话框,然后在返回方法中使用dialog.dismiss()强制清理它以防止泄漏。

虽然在我所知的任何地方都没有明确说明,但似乎你只是为了执行此操作而传回OnClickHandlers中的对话框。

答案 26 :(得分:0)

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

答案 27 :(得分:0)

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

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

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

在适配器上接收:

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

现在您可以在Builder上使用

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

答案 28 :(得分:0)

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

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

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

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

大卫