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

发布于 2024-11-04 03:22:11 字数 1355 浏览 11 评论 0原文

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

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

但是,我对使用“this”作为上下文持怀疑态度,因为即使在屏幕旋转等简单的情况下,当 Activity 被销毁并重新创建时,也可能会出现内存泄漏。来自Android 开发者博客上的相关帖子

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

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

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

参考文献:1、23 等。

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

吉姆所宣传的那样起作用?

My Activity is trying to create an AlertDialog which requires a Context as a parameter. This works as expected if I use:

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

However, I am leery of using "this" as a context due to the potential for memory leaks when Activity is destroyed and recreated even during something simple like a screen rotation. From a related post on the Android developer's blog:

There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid escaping the context outside of its own scope. The example above showed the case of a static reference but inner classes and their implicit reference to the outer class can be equally dangerous. The second solution is to use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. If you plan on keeping long-lived objects that need a context, remember the application object. You can obtain it easily by calling Context.getApplicationContext() or Activity.getApplication().

But for the AlertDialog() neither getApplicationContext() or getApplication() is acceptable as a Context, as it throws the exception:

"Unable to add window — token null is not for an application”

per references: 1, 2, 3, etc.

So, should this really be considered a "bug", since we are officially advised to use Activity.getApplication() and yet it doesn't function as advertised?

Jim

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(28

舟遥客 2024-11-11 03:22:11

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

Instead of getApplicationContext(), just use ActivityName.this.

反话 2024-11-11 03:22:11

使用 this 对我不起作用,但 MyActivityName.this 对我有用。

Using this did not work for me, but MyActivityName.this did.

茶色山野 2024-11-11 03:22:11

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

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

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

You can continue to use getApplicationContext(), but before use, you should add this flag: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT), and the error will not show.

Add the following permission to your manifest:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
千秋岁 2024-11-11 03:22:11

当您说“...对于 AlertDialog() 来说, getApplicationContext() 或 getApplication() 都不能作为上下文接受时,您已经正确地识别了问题,因为它抛出异常:'无法添加窗口 - 令牌 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”(或其他引用)无限期地引用。还要记住检查链引用。您可以使用以下一些工具来帮助您分析内存使用情况并查找那些讨厌的内存泄漏:

You've correctly identified the problem when you said "... for the AlertDialog() neither getApplicationContext() or getApplication() is acceptable as a Context, as it throws the exception: 'Unable to add window — token null is not for an application'"

To create a Dialog, you need an Activity Context or a Service Context, not an Application Context (both getApplicationContext() and getApplication() return an Application Context).

Here's how you get the Activity Context:

(1) In an Activity or a Service:

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

(2) In a Fragment:
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

Memory leaks is not a problem that is intrinsic to the "this" reference, which is an object's reference to itself (i.e. reference to the actual allocated memory for storing the object's data). It happens to any allocated memory for which the Garbage Collector (GC) is unable to free up after the allocated memory has outlived its useful lifespan.

Most of the time, when a variable goes out of scope, the memory will be reclaimed by the GC. However, memory leaks can occur when the reference to an object held by a variable, say "x", persists even after the object has outlived its useful lifespan. The allocated memory will hence be lost for as long as "x" holds a reference to it because GC will not free up the memory for as long as that memory is still being referenced. Sometimes, memory leaks are not apparent because of a chain of references to the allocated memory. In such a case, the GC will not free up the memory until all references to that memory have been removed.

To prevent memory leaks, check your code for logical errors that cause allocated memory to be referenced indefinitely by "this" (or other references). Remember to check for chain references as well. Here are some tools you can use to help you analyze memory use and find those pesky memory leaks:

孤独患者 2024-11-11 03:22:11

您的对话框不应该是“需要上下文的长期对象”。文档很混乱。基本上,如果您执行类似以下操作:(

static Dialog sDialog;

注意静态

然后在您执行的某处活动中,

 sDialog = new Dialog(this);

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

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

Dialog mDialog;

...

mDialog = new Dialog(this);

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

Your dialog should not be a "long-lived object that needs a context". The documentation is confusing. Basically if you do something like:

static Dialog sDialog;

(note the static)

Then in an activity somewhere you did

 sDialog = new Dialog(this);

You would likely be leaking the original activity during a rotation or similar that would destroy the activity. (Unless you clean up in onDestroy, but in that case you probably wouldn't make the Dialog object static)

For some data structures it would make sense to make them static and based off the application's context, but generally not for UI related things, like dialogs. So something like this:

Dialog mDialog;

...

mDialog = new Dialog(this);

Is fine and shouldn't leak the activity as mDialog would be freed with the activity since it's not static.

最好是你 2024-11-11 03:22:11

Activity中只需使用:

MyActivity.this

Fragment:

getActivity();

in Activity just use:

MyActivity.this

in Fragment:

getActivity();
最好是你 2024-11-11 03:22:11

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

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

I had to send my context through a constructor on a custom adapter displayed in a fragment and had this issue with getApplicationContext(). I solved it with:

this.getActivity().getWindow().getContext() in the fragments' onCreate callback.

酷遇一生 2024-11-11 03:22:11

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

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

***** kotlin version *****

You should pass this@YourActivity instead of applicationContext or baseContext

不打扰别人 2024-11-11 03:22:11

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

Dialog dialog = new Dialog(MyActivity.this);

对我有用。

In Activity on click of button showing a dialog box

Dialog dialog = new Dialog(MyActivity.this);

Worked for me.

峩卟喜欢 2024-11-11 03:22:11

只需使用以下内容:

对于 JAVA 用户

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

OR

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

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

对于 KOTLIN 用户

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

val builder = AlertDialog.Builder(this@your_activity)

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

Just use following:

FOR JAVA USERS

In case you are using activity --> AlertDialog.Builder builder = new AlertDialog.Builder(this);

OR

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

In case you are using fragment --> AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());

FOR KOTLIN USERS

In case you are using activity --> val builder = AlertDialog.Builder(this)

OR

val builder = AlertDialog.Builder(this@your_activity)

In case you are using fragment --> val builder = AlertDialog.Builder(requireActivity())

っ左 2024-11-11 03:22:11

小窍门:您可以防止 GC 破坏您的 Activity(您不应该这样做,但在某些情况下它会有所帮助。当不再需要):

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

Little hack: you can prevent destroying your activity by GC (you should not do it, but it can help in some situations. Don't forget to set contextForDialog to null when it's no longer needed):

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();
    }
    ...
}
风蛊 2024-11-11 03:22:11

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

像这样

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

If you are using a fragment and using AlertDialog/Toast message then use getActivity() in the context parameter.

like this

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();
忆悲凉 2024-11-11 03:22:11

添加

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

在清单中

"android.permission.SYSTEM_ALERT_WINDOW"/>现在对我有用。即使关闭并打开应用程序后,当时还是给了我错误。

adding

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

and

"android.permission.SYSTEM_ALERT_WINDOW"/> in manifest

It works for me now. After even close and open the application, gave me the error at that time.

国产ˉ祖宗 2024-11-11 03:22:11

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

对我有用的解决方案是传递 getActivity();即

progressDialog = new ProgressDialog(getActivity());

I was using ProgressDialog in a fragment and was getting this error on passing getActivity().getApplicationContext() as the constructor parameter. Changing it to getActivity().getBaseContext() didn't work either.

The solution that worked for me was to pass getActivity(); i.e.

progressDialog = new ProgressDialog(getActivity());

离去的眼神 2024-11-11 03:22:11

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

Use MyDialog md = new MyDialog(MyActivity.this.getParent());

黯然 2024-11-11 03:22:11

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

If you are outside of the Activity then you need to use in your function "NameOfMyActivity.this" as Activity activity, example:

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);
你丑哭了我 2024-11-11 03:22:11

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

为我工作。

干杯!

If you are using a fragment and using an AlertDialog / Toast message, use getActivity() in the context parameter.

Worked for me.

Cheers!

半山落雨半山空 2024-11-11 03:22:11

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

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

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

Try to use the context of an activity which will be under the dialog. But be carefull when you use "this" keyword, because it will not work everytime.

Forexample, if you have TabActivity as host with two tabs, and each tab is another activity, and if you try to create dialog from one of the tabs (activities) and if you use "this", then you will get exception,
In this case dialog should be connected to host activity which host everything and visible. (you can say most visible parent Activity's context)

I did not find this info from any document but by trying. This is my solution without strong background, If anybody with better knownledge, feel free to comment.

零崎曲识 2024-11-11 03:22:11

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

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

For future readers, this should help:

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}
聆听风音 2024-11-11 03:22:11

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

Try getParent() at the argument place of context like new AlertDialog.Builder(getParent()); Hope it will work, it worked for me.

守望孤独 2024-11-11 03:22:11

就我而言,工作:

this.getContext();

In my case work:

this.getContext();
傻比既视感 2024-11-11 03:22:11

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

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

Or another possibility is to create Dialog as follow:

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));
悲喜皆因你 2024-11-11 03:22:11

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

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

I think it may happen as well if you are trying to show a dialog from a thread which is not the main UI thread.

Use runOnUiThread() in that case.

许你一世情深 2024-11-11 03:22:11

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

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

After taking a look at the API, you can pass the dialog your activity or getActivity if you're in a fragment, then forcefully clean it up with dialog.dismiss() in the return methods to prevent leaks.

Though it is not explicitly stated anywhere I know, it seems you are passed back the dialog in the OnClickHandlers just to do this.

纵山崖 2024-11-11 03:22:11

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

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

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

在适配器上接收:

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

现在您可以在构建器上使用

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

If your Dialog is creating on the adapter:

Pass the Activity to the Adapter Constructor:

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

Receive on the Adapter:

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

Now you can use on your Builder

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);
一杯敬自由 2024-11-11 03:22:11

伙计们,我有一个简单的备忘单。
创建一个文件,给它任何名称,然后在其中编写此代码

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

Goodluck
对于科特林

Guys I got a simple cheat sheet.
create a file give it any name then in it write this code

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

now when you need to show an alert dialog only use this method anywhere

requireActivity().alertdialog(requireContext(), resources.getString(R.string.pass_title),
                resources.getDrawable(R.drawable.pass_ic_name), "title")

Goodluck
for Kotlin

何以笙箫默 2024-11-11 03:22:11

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

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

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

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

使用 Activity[可选名称]: Activity[强制类型] 直到它到达您想要膨胀的对话框

它需要大量传递,但它确实比到处复制和粘贴相同的代码更有意义

What worked for me was to pass the activity instead of the context.

I wanted a custom layout for my dialog, but to keep my code separate, I created it in a separate Class, else I would have to copy and paste that chunk of code into every activity where I want to use the dialog.

Solution explains my situation but it gives the core solution:

  1. As I was using a ViewAdapter I initialised the adapter with the Activity (not context ex. ->[kotlin] activity: Activity) as a parameter -> ex. [kotlin] this@MainActivity
  2. Then I passed that parameter to the Viewholder
  3. After which passing it again to the class that will inflate the Dialog.

Use the activity[optional name]: Activity[mandatory type] every where until it gets to the dialog you want to inflate

Its a lot of passing around, but it does make more sense over copy and pasting the same code everywhere

暗藏城府 2024-11-11 03:22:11

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

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

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

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

大卫

Here is how I resolved same error for my application:
Adding the following line after creating the dialog:

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

You will not need to acquire a context. This is particularly useful if you are popping up another dialog over current popped up dialog. Or when it's not convenient to get a context.

Hope this can help you with your app development.

David

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文