使用getApplication()作为上下文时,对话框抛出“无法添加窗口-令牌为空不是应用程序”的错误。

695

我的Activity试图创建一个需要Context作为参数的AlertDialog。如果我使用以下代码,则可以正常工作:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
然而,我对使用"this"作为上下文感到不安,因为即使在像屏幕旋转这样简单的事情中,Activity被销毁和重新创建时可能会出现内存泄漏的风险。来自Android开发者博客上一篇相关文章的说法如下:

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

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

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

相关参考:123等。
那么,既然我们被官方建议使用Activity.getApplication(),但实际上它没有按照广告所说的正常工作,那么这真的应该被认为是一个“bug”吗?
Jim

第一项建议使用getApplication的参考资料:http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html - gymshoe
其他参考资料:https://dev59.com/GHI_5IYBdhLWcg3wBuRs - gymshoe
1
请参考以下内容:https://dev59.com/aXE85IYBdhLWcg3wvGKm - gymshoe
http://groups.google.com/group/android-developers/browse_thread/thread/7a648edddccf6f7d - gymshoe
显示剩余2条评论
28个回答

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

@MurtuzaKabul 这段代码能够正常工作是因为 this == PostActivity,而 PostActivity 继承自 Activity -> Context,所以当你将对话框所需的上下文传递给它时,实际上是传递了 activity。 - Elad Gelman

13
如果您正在使用片段并使用AlertDialog/Toast消息,则在上下文参数中使用getActivity()。
例如:
ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

9
添加
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

并且需要在manifest文件中添加"android.permission.SYSTEM_ALERT_WINDOW"/>权限。

现在它对我有效了。即使关闭并重新打开应用程序,在那时也会给我错误提示。


9

我在一个fragment中使用了ProgressDialog,并且在将getActivity().getApplicationContext()作为构造函数参数传递时出现了错误。将其更改为getActivity().getBaseContext()也没有起作用。

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

progressDialog = new ProgressDialog(getActivity());


6
如果您在Activity之外,则需要在函数中使用“NameOfMyActivity.this”作为Activity 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);

6

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

该代码用于创建一个名为MyDialog的对话框,其父活动为MyActivity。

5

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

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

5

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

对我来说很有用。

谢谢!


5
尝试使用一个对话框下的活动的上下文。但是当您使用“this”关键字时要小心,因为它并不总是有效。
例如,如果您有一个带有两个选项卡的TabActivity作为主机,每个选项卡都是另一个活动,并且如果您尝试从其中一个选项卡(活动)创建对话框并使用“this”,那么您将会得到异常, 在这种情况下,对话框应连接到托管所有内容和可见的主机活动。 (您可以说最可见父活动的上下文)
我没有在任何文件中找到这些信息,而是通过尝试找到的解决方案。这是我的解决方案,没有强大的背景知识。如果有更好的知识,请随意评论。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接