【问题标题】:Dialog throwing "Unable to add window — token null is not for an application” with getApplication() as context对话框抛出“无法添加窗口 - 令牌 null 不适用于应用程序”,getApplication() 作为上下文
【发布时间】:2020-09-17 21:38:22
【问题描述】:

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

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

但是,我对使用“this”作为上下文持怀疑态度,因为即使在屏幕旋转之类的简单操作中,当 Activity 被销毁和重新创建时也可能发生内存泄漏。来自related post on the Android developer's blog

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

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

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

每个引用:123

那么,既然官方建议我们使用Activity.getApplication(),但它并没有像宣传的那样发挥作用,那么这真的应该被视为一个“错误”吗?

吉姆

【问题讨论】:

标签: android android-alertdialog android-context builder


【解决方案1】:

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

【讨论】:

  • 太棒了!只是对此发表评论..您有时可能需要全局存储“this”(例如),以便在拥有自己的“this”的侦听器实现的方法中访问它。在这种情况下,您将全局定义“上下文上下文”,然后在 onCreate 中设置“context = this”,然后引用“上下文”。希望这也能派上用场。
  • 实际上,由于Listener 类通常是匿名内部的,我倾向于只做final Context ctx = this; 并且我离开了;)
  • @StevenL 为了做到你所说的,你应该使用 ExternalClassName.this 来明确引用外部类的“this”。
  • 如果您的对话框在回调中使用并且您在调用回调之前离开活动,则不会使用“this”泄漏它?至少这就是 Android 在 logcat 中似乎抱怨的地方。
  • 我不建议使用@StevenLs 方法,因为您很容易泄漏该活动的内存,除非您记得清除 onDestroy 中的静态引用 - Artem 是正确的。 StevenL 的方法源于缺乏对 Java 工作原理的理解
【解决方案2】:

使用this 对我不起作用,但MyActivityName.this 对我有用。希望这可以帮助那些无法让this 工作的人。

【讨论】:

  • 当您在内部类内部使用this 时会发生这种情况。如果要引用外部类的实例,则必须指定,就像使用 OuterClass.this 一样。只使用this 总是引用最内部类的实例。
【解决方案3】:

你可以继续使用getApplicationContext(),但是在使用之前,你应该加上这个标志:dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),这样错误就不会出现了。

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

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

【讨论】:

  • 我无法添加窗口 android.view.ViewRootImpl$W@426ce670 -- 此窗口类型的权限被拒绝
  • 添加权限:
  • 似乎您无法在 API 23 及更高版本中启用此权限code.google.com/p/android-developer-preview/issues/…
  • 您可以将其用于 API 23 及更高版本,但是您需要提示用户: startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), OVERLAY_PERMISSION_REQ_CODE );但是,您是否应该使用它是另一回事...
  • 这在您在服务中显示进度对话框时很有用
【解决方案4】:

当您说“......对于 AlertDialog() 而言,getApplicationContext() 或 getApplication() 都不能作为上下文接受,因为它会引发异常:'无法添加窗口 - 令牌 null不适用于应用程序'"

要创建 Dialog,您需要 Activity ContextService Context,而不是 Application Context(getApplicationContext() 和 getApplication () 返回一个应用程序上下文)。

获取活动上下文的方法如下:

(1) 在活动或服务中:

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

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

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

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

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

【讨论】:

  • 对于一个 Activity,您还可以使用 ActivityName.this 其中 ActivityName 是(显然)您的 Activity 的名称(例如 MainActivity)
【解决方案5】:

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

static Dialog sDialog;

(注意静态

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

 sDialog = new Dialog(this);

您可能会在轮换或类似情况下泄露原始活动,从而破坏活动。 (除非您在 onDestroy 中进行清理,但在这种情况下您可能不会将 Dialog 对象设为静态)

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

【讨论】:

  • 我从异步任务中调用它,这对我有用,谢谢队友
  • 我的对话框是静态的,一旦我删除了它的静态声明。
【解决方案6】:

Activity中使用:

MyActivity.this

片段中:

getActivity();

【讨论】:

  • 这在我的活动中为我修复了它。谢谢
【解决方案7】:

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

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

【讨论】:

  • 这对我也有用,我将它传递给我正在使用的外部 AsyncTask 的构造函数(它显示了一个进度对话框)。
  • 这是更复杂任务的真正答案:)
  • 我同意@teejay
【解决方案8】:

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

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

【讨论】:

    【解决方案9】:

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

    Dialog dialog = new Dialog(MyActivity.this);
    

    为我工作。

    【讨论】:

      【解决方案10】:

      小技巧:您可以防止 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();
          }
          ...
      }
      

      【讨论】:

      • @MurtuzaKabul 之所以有效,是因为 this == PostActivity 继承自 Activity-> 继承自 Context,因此当您传递对话框时,您的上下文实际上是在传递 Activity
      【解决方案11】:

      只需使用以下内容:

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

      【讨论】:

        【解决方案12】:

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

        喜欢这个

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

        【讨论】:

          【解决方案13】:

          添加

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

          "android.permission.SYSTEM_ALERT_WINDOW"/&gt; 在清单中

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

          【讨论】:

            【解决方案14】:

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

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

            progressDialog = new ProgressDialog(getActivity());

            【讨论】:

              【解决方案15】:

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

              【讨论】:

                【解决方案16】:

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

                【讨论】:

                  【解决方案17】:

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

                  为我工作。

                  干杯!

                  【讨论】:

                    【解决方案18】:

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

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

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

                    【讨论】:

                      【解决方案19】:

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

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

                      【讨论】:

                        【解决方案20】:

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

                        【讨论】:

                          【解决方案21】:

                          就我而言:

                          this.getContext();
                          

                          【讨论】:

                            【解决方案22】:

                            或者另一种可能性是创建对话框如下:

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

                            【讨论】:

                              【解决方案23】:

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

                              在这种情况下使用runOnUiThread()

                              【讨论】:

                                【解决方案24】:

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

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

                                【讨论】:

                                  【解决方案25】:

                                  如果您的 Dialog 正在适配器上创建:

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

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

                                  在适配器上接收:

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

                                  现在您可以在 Builder 上使用

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

                                  【讨论】:

                                    【解决方案26】:

                                    伙计们,我有一个简单的备忘单。 创建一个文件,给它起任何名字,然后在里面写下这段代码

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

                                    祝你好运 对于科特林

                                    【讨论】:

                                      【解决方案27】:

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

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

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

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

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

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

                                      【讨论】:

                                        【解决方案28】:

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

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

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

                                        希望这对您的应用开发有所帮助。

                                        大卫

                                        【讨论】:

                                          猜你喜欢
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 1970-01-01
                                          • 2012-09-12
                                          • 2011-05-28
                                          • 2019-01-04
                                          • 2011-12-17
                                          相关资源
                                          最近更新 更多