首页 文章

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

提问于
浏览
606

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

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() 都不能作为Context接受,因为它会抛出异常:

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

每个参考:123

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

吉姆

24 回答

  • 2

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

  • 1

    您无法通过非活动或服务的上下文显示应用程序窗口/对话框 . 尝试传递有效的活动参考

  • 1249

    在我的案例中工作:

    this.getContext();
    
  • 54

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

    像这样

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

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

  • 1

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

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

  • 4

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

  • 5

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

    static Dialog sDialog;
    

    (注意 static

    然后在某个地方进行的活动

    sDialog = new Dialog(this);
    

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

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

    Dialog mDialog;
    
    ...
    
    mDialog = new Dialog(this);
    

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

  • 15

    使用 this 对我不起作用,但 MyActivityName.this 确实如此 . 希望这有助于任何无法工作的人.113408_ .

  • 18

    小黑客:你可以防止通过GC破坏活动(当然,你不应该这样做,但它可以在某些情况下帮助):

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

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

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

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

  • 8

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

    将以下权限添加到清单:

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

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

    Dialog dialog = new Dialog(MyActivity.this);
    

    为我工作 .

  • 12

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

    为我工作 .

    干杯!

  • 2

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

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

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

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

  • 35

    加入

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

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

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

  • 5

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

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

    progressDialog = new ProgressDialog(getActivity());

  • 29

    如果您不在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);
    
  • 172

    Activity 中使用:

    MyActivity.this
    

    Fragment:

    getActivity();
    
  • 1

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

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

  • 1

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

    要创建对话框,您需要 Activity ContextService Context ,而不是 Application Context (getApplicationContext()和getApplication()都返回应用程序上下文) .

    这是你如何得到 Activity Context

    (1)在活动或服务中:

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

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

    内存泄漏不是"this"引用固有的问题,它是一个对象's reference to itself (i.e. reference to the actual allocated memory for storing the object'的数据) . 它发生在任何已分配的内存中,垃圾收集器(GC)在分配的内存耗尽其使用寿命后无法释放 .

    大多数情况下,当变量超出范围时,GC将回收内存 . 但是,即使在对象已超过其使用寿命之后,对变量持有的对象(例如"x")的引用仍然存在时,可能会发生内存泄漏 . 只要"x"拥有对它的引用,分配的内存就会丢失,因为只要该内存仍被引用,GC就不会释放内存 . 有时,由于分配的内存 a chain of references ,内存泄漏不明显 . 在这种情况下,GC将不会释放内存,直到删除了对该内存的所有引用 .

    为防止内存泄漏,请检查代码中是否存在导致已分配内存被“this”(或其他引用)无限引用的逻辑错误 . 记得检查链引用 . 以下是一些可用于帮助您分析内存使用并找到那些讨厌的内存泄漏的工具:

  • 15

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

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

    我想如果你试图从一个不是主UI线程的线程显示一个对话框,也可能会发生这种情况 .

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

相关问题