Android:ProgressDialog.show()在调用getApplicationContext时崩溃

111

我似乎无法理解为什么会发生这种情况。这段代码:

mProgressDialog = ProgressDialog.show(this, "", getString(R.string.loading), true);

代码可以正常工作。然而,以下代码:

mProgressDialog = ProgressDialog.show(getApplicationContext(), "", getString(R.string.loading), true);

抛出以下异常:

W/WindowManager(  569): Attempted to add window with non-application token WindowToken{438bee58 token=null}.  Aborting.
D/AndroidRuntime( 2049): Shutting down VM
W/dalvikvm( 2049): threadid=3: thread exiting with uncaught exception (group=0x4001aa28)
E/AndroidRuntime( 2049): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime( 2049): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tastekid.TasteKid/com.tastekid.TasteKid.YouTube}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.access$2100(ActivityThread.java:116)
E/AndroidRuntime( 2049):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
E/AndroidRuntime( 2049):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 2049):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.main(ActivityThread.java:4203)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
E/AndroidRuntime( 2049):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 2049): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.view.ViewRoot.setView(ViewRoot.java:460)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
E/AndroidRuntime( 2049):    at android.app.Dialog.show(Dialog.java:238)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:107)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:90)
E/AndroidRuntime( 2049):    at com.tastekid.TasteKid.YouTube.onCreate(YouTube.java:45)
E/AndroidRuntime( 2049):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
E/AndroidRuntime( 2049):    ... 11 more
任何想法为什么会发生这种情况?我是从onCreate方法中调用它的。

如果您正在使用Fragment,请参考https://dev59.com/-GAf5IYBdhLWcg3wSQ8z。 - Yeo
18个回答

129

我使用的是Android 2.1版本,API级别为7。我遇到了这个(或类似的)问题,并通过以下方法解决:

Dialog dialog = new Dialog(this);

不要这样写:

Dialog dialog = new Dialog(getApplicationContext());

4
我有一个类似的问题,但是我在使用ActivityGroup。我解决这个错误的唯一方法是使用getParent()。 - brack
22
这个回答如何解答他的问题?他问为什么第二个不起作用,而第一个可以,你需要说明原因,但不要改变原意。 - Burkhard
3
对于我们中的一些人来说,“this”就像“getApplicationContext()”一样。 - kellogs
仍然是一个有用的提示,针对2.1版本进行开发。 - Carlos P

64
对于我来说,更改以下内容起作用:
builder = new AlertDialog.Builder(getApplicationContext());

为了

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

奇怪的是,第一个可以在Google教程中找到,但人们在使用时会出现错误。

1
唯一对我有效的解决方案是在onclick事件内部使用警报。 - Tobias
这是做事的正确方式。除非绝对必要,否则不要使用ApplicationContext,并且最重要的是,不要使用它来显示UI元素。这就是活动(最终是片段)的作用。 - Martin Marconcini
这就是答案。如果你在例如点击监听器中使用this,它本身是不起作用的。 - Ayman Salah

42
你使用的是哪个API版本?如果我对问题的猜测正确的话,那么这个问题在Android 1.6(API版本4)中已经被修复了。
看起来,getApplicationContext()返回的对象引用指向null。我认为你遇到了类似于我遇到过的问题,即onCreate()中的某些代码在窗口实际构建完成之前就已经运行。这将是一种hack方法,但可以尝试在几百毫秒后启动一个新线程(IIRC:300-400对我有效,但你需要调整),打开ProgressDialog并开始任何其他需要的操作(例如网络IO)。像这样:
@Override
public void onCreate(Bundle savedInstanceState) {
    // do all your other stuff here

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            mProgressDialog = ProgressDialog.show(
               YouTube.this.getApplicationContext(), "",
               YouTube.this.getString(R.string.loading), true);

            // start time consuming background process here
        }
    }, 1000); // starting it in 1 second
}

6
我正在使用1.6版本。我非常确定任何UI操作都应该在UI线程中完成,因此在单独的线程中调用ProgressDialog.show()可能会很麻烦。但我仍然认为这很奇怪。 - Felix
3
在我给你的例子中,你并没有在另一个线程中执行UI操作,另一个线程只是回调到UI线程告诉它打开对话框。 - Jeremy Logan
1
这不是“奇怪”的,也不是“彻底的hack”,而是一种完全合法的方法! - KomodoDave
1
@KomodoDave 我刚意识到我也有同样的问题。然而,这个糟糕的hack在计时器中。这是一个等待定时窗口,在某些情况下间歇性地失败。成功的关键可能在于设置一个较短的计时器,检查应用程序是否准备好,重新延迟操作直到准备就绪。还应该限制尝试的次数。 - Jim Rush
这并不奇怪,他正在使用 Handler。我不会创建另一个线程来显示警告对话框,但这确实取决于你想要做什么。 - Martin Marconcini
显示剩余2条评论

23

我认为这不是空应用程序上下文的时间问题。

尝试在您的应用程序中扩展Application(如果您已经有,则只需使用它)。

public class MyApp extends Application

将实例作为私有单例模式提供。这个实例永远不会是null

private static MyApp appInstance;
在MyApp中创建一个静态帮助程序(它将使用单例模式)。
    public static void showProgressDialog( CharSequence title, CharSequence message )
{
    prog = ProgressDialog.show(appInstance, title, message, true); // Never Do This!
}

BOOM!!

同时,可以在此处查看 android 工程师的答案:WindowManager$BadTokenException

这种错误的原因之一可能是尝试通过不是 Activity 的 Context 显示应用程序窗口/对话框。

现在,我同意,方法接收 Context 参数而不是 Activity,这确实没有意义。


10

在阅读了上面的答案后,我发现对于我的情况,以下内容解决了问题。

这导致了错误。

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(getApplicationContext());
        dialog.show();              
    }
});

根据之前建议上下文错误的答案,我将getApplicationContext()改为从传递给按钮onClick方法的视图中检索上下文。

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(v.getContext());
        dialog.show();              
    }
});

我并不完全了解Java的工作原理,所以可能是错的,但我猜想对于我的特定情况,上面的代码片段被定义在一个抽象Activity类中;被多个Activity继承和使用,也许这就导致getApplicationContext()方法不能返回有效的上下文?(仅仅是猜测)。


更好的评分解决方案可能适用于大多数情况,但您的v.getContext()技术似乎可以解决像我这样最顽固的情况。我的经验性Java知识让我认为,来自onCreated方法的上下文与来自onClick方法的View的上下文并不完全相同。点赞! - Josh
这个救了我的一天! - Alejandro Luengo

6

我正在创建一个带有标记覆盖物的地图视图。在我的MapActivity中,我像这样创建了我的ItemizedOverlay:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

我发现当我的itemizedoverlay的onTap方法被触发时(当在mapview上点击位置时),我会得到"android.view.WindowManager $ BadTokenException:无法添加窗口-- token null不是应用程序"异常。
如果我只是简单地传递'this'而不是'getApplicationContext()'到我的构造函数,这个问题就解决了。这似乎支持alienjazzcat的结论。奇怪。

4
在TabActivities中展示的活动,使用getParent()
final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());

代替
final AlertDialog.Builder builder = new AlertDialog.Builder(this);

3

对于Android 2.2
请使用以下代码:

//activity is an instance of a class which extends android.app.Activity
Dialog dialog = new Dialog(activity);

改写这段代码:

// this code produces an ERROR:
//android.view.WindowManager$BadTokenException: 
//Unable to add window -- token null is not for an application
Context mContext = activity.getApplicationContext();
Dialog dialog = new Dialog(mContext);

备注:我的自定义对话框是在activity.onCreateDialog(int dialogId)方法之外创建的。


3

尝试 -

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

当当前活动位于ActivityGroup内时需要使用。 - htafoya

2

要在活动中使用对话框,请按照以下方式操作:

private Context mContext;
private AlertDialog.Builder mBuilder;

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     mContext = this;

     //using mContext here refering to activity context
     mBuilder = new AlertDialog.Builder(mContext);
     //...
     //rest of the code
     //...
}

如果要在片段中使用对话框,请按照以下方式处理:

private Context mContext;
private AlertDialog.Builder mBuilder;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View mRootView = inflater.inflate(R.layout.fragment_layout, container, false);
      mContext = getActivity();

      //using mContext here refering to fragment's hosting activity context
      mBuilder = new AlertDialog.Builder(mContext);
      //...
      //rest of the code
      //...
      return mRootView;
}

That's it ^_^


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