简体   繁体   中英

android.view.WindowManager$BadTokenException error while creating dialog box

I am trying to create a Dialog Box from an Non activity Class.

This is my Code

  public static void ShowDialogBox(final Context con, final Listener list) {
        AlertDialog.Builder dlgAlert = new AlertDialog.Builder(con);
        dlgAlert.setMessage("TEXT");
        dlgAlert.setTitle("TEXT");
        dlgAlert.setPositiveButton("TEXT"),
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });
        dlgAlert.setCancelable(false);
        dlgAlert.create().show(); // THIS LINE GIVES ME AN ERROR
    }

This is the error which I am getting

android.view.WindowManager$BadTokenException: at android.view.ViewRootImpl.setView (ViewRootImpl.java:574) at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:282) at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:85)
at android.app.Dialog.show (Dialog.java:298) at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS PACKAGE NAME AND CLASS at PACKAGE NAME AND CLASS.onBackPressed (Class.java:95) at android.app.Activity.onKeyUp (Activity.java:2465) at android.view.KeyEvent.dispatch (KeyEvent.java:2646) at android.app.Activity.dispatchKeyEvent (Activity.java:2716) at android.support.v7.internal.view.WindowCallbackWrapper.dispatchKeyEvent (WindowCallbackWrapper.java:50) at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.dispatchKeyEvent (AppCompatDelegateImplBase.java:224) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent (PhoneWindow.java:2280) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent (ViewRootImpl.java:4038) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:4000) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage. onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3698) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589)
at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:3755) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:3589) at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:3562) at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:3615) at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:3581) at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:3731) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent (ViewRootImpl.java:3892) at android.view.inputmethod.InputMethodManager$PendingEvent.run (InputMethodManager.java:2208) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback (InputMethodManager.java:1849) at android.view.inputmetho d.InputMethodManager.finishedInputEvent (InputMethodManager.java:1840) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished (InputMethodManager.java:2185) at android.view.InputEventSender.dispatchInputEventFinished (InputEventSender.java:141) at android.os.MessageQueue.nativePollOnce (Native Method) at android.os.MessageQueue.next (MessageQueue.java:143) at android.os.Looper.loop (Looper.java:122) at android.app.ActivityThread.main (ActivityThread.java:5254) at java.lang.reflect.Method.invoke (Native Method) at java.lang.reflect.Method.invoke (Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:902) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:697)

Here is the scenario of the user

Activity A -->Opens Activity B-->User presses Back button in Activity B--> On Back button pressed a listener is sent to Activity A --> And then the Dialog Box shown is called.

I generally prefer using DialogFragment instead of what you attempted in order to cut down on repetition. Here is an example of a DialogFragment with a custom layout which I have called R.layout.fragment_alert_dialog :

public class AlertDialogFragment extends DialogFragment {

    private static final String ARG_TITLE = "title";
    private static final String ARG_MESSAGE = "message";

    private String title;
    private String message;
    boolean endSuccess = false;

    private AlertFinishedDialogListener mListener;

    public AlertDialogFragment() {
    }

    public static AlertDialogFragment newInstance(String title, String message) {
        AlertDialogFragment fragment = new AlertDialogFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        args.putString(ARG_MESSAGE, message);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            title = getArguments().getString(ARG_TITLE);
            message = getArguments().getString(ARG_MESSAGE);
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle saveIntsanceState){

        final Context context = getActivity();

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

        LayoutInflater inflater = getActivity().getLayoutInflater();

        View rootView = inflater.inflate(R.layout.fragment_alert_dialog, null, false);
        final TextView titleView = (TextView)rootView.findViewById(R.id.tvTitle);
        final TextView messView = (TextView)rootView.findViewById(R.id.tvMessage);

        titleView.setText(title);
        messView.setText(message);

        builder.setView(rootView)
//                .setTitle(title)
                .setPositiveButton(R.string.ok_button_dialog_title, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        endSuccess = true;
                        if(mListener == null) mListener = (AlertFinishedDialogListener) context;
                        mListener.onAlertFinishedDialog();
                    }
                });
        return builder.create();
    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            if(mListener == null) mListener = (AlertFinishedDialogListener) context;
        }
        catch (Exception ex){
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public interface AlertFinishedDialogListener {
        void onAlertFinishedDialog();
    }
}

It incorporates a Listener just in case you need to be notified when the DialogFragment is completed.

First you need to implement the callback: implements AlertDialogFragment.AlertFinishedDialogListener{

In order to call the AlertDialogFragment you do this from your Activity (also Fragment if necessary).

private void startAlertDialogFragment(String title, String mess){
    AlertDialogFragment alert = AlertDialogFragment.newInstance(title, mess);
    alert.show(getFragmentManager(), "alertDialogFragment132");
}

@Override
public void onAlertFinishedDialog() {
    Log.e(TAG, "onAlertFinishedDialog");
}

Problem

You can show dialogs from activity context only. except TYPE_SYSTEM_ALERT or TYPE_APPLICATION_OVERLAY , which is not recommended if your app does not show emergency notifications to user.

Solution

If you have activity context available then you can show dialog from any class like service , broadcast receiver , or even any class you imagine.

Here is my workaround that can show dialog from any class like i said.

Here is a snippet what i do to show dialog from any class. ( Could it be more simpler! )

import android.app.Dialog;
import android.content.DialogInterface;

public class SampleClass {
    void anyMethod() {
        Dialog dialog = ApplicationContext.getInstance().showDialog("title", "yourMessage", "Cancel", "Ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 1 clicked 
            }
        }, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO: handle button 2 clicked
            }
        });
    }
}

Now you will implement to make this work.

1. make application class which you will register in android manifest application tag

  <application
    android:name=".ApplicationContext"
    ...
    >
      ...
  </application>

2. In this application class you will hold live activity object. that will further useful for showing dialog.

ApplicationContext.java

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.content.DialogInterface;


public class ApplicationContext extends Application {
    private static ApplicationContext mInstance;
    private Activity liveActivity;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        mInstance = null;
    }

    public static synchronized ApplicationContext getInstance() {
        return mInstance;
    }

    public Activity getLiveActivity() {
        return liveActivity;
    }

    public void setLiveActivity(Activity liveActivity) {
        this.liveActivity = liveActivity;
    }

    /*
     * Show Dialog with Title, Message, Button1, Button2 with Button1 and Button2 Listener
     */
    public AlertDialog showDialog(String title, String msg,
                                  String btn1, String btn2,
                                  DialogInterface.OnClickListener listener1,
                                  DialogInterface.OnClickListener listener2) {
        if (liveActivity == null) return null;
        AlertDialog.Builder builder = new AlertDialog.Builder(liveActivity)
                .setTitle(title)
                .setMessage(msg)
                .setCancelable(false)
                .setPositiveButton(btn1, listener1);
        if (btn2 != null)
            builder.setNegativeButton(btn2, listener2);

        AlertDialog alert = builder.create();
        alert.show();
        return alert;
    }
}

Just one more step

3. You will extend all your activity by this base activity class (You can edit your base activity if you already have one.)

import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onResume() {
        super.onResume();
        ApplicationContext.getInstance().setLiveActivity(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        ApplicationContext.getInstance().setLiveActivity(null);
    }
}

Here you go !!!

The issue you are having with the attempt to build your AlertDialog in a separate class is you are passing the AlertDialog the Context of your Activity . You get the error because the AlertDialog requires the WindowManager from the Activity which has the layout--not the Context . This is because Activit extends Context ... not the other way around.

In order to make your code work you need to provide the AlertDialog.Builder access to the Activity . So change your code to something like this:

public class TestDialog {

    private static final String TAG = TestDialog.class.getSimpleName();

    Activity mActivity;

    public TestDialog(Activity activity){
        mActivity = activity;
    }


    public void showDialog(){
        AlertDialog.Builder b = new AlertDialog.Builder(mActivity);
        b.setTitle("Title");
        b.setMessage("message");
        b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.e(TAG, "showDialog : onClick");
            }
        });
        b.create().show();
    }
}

Now you can call the AlertDialog from an Activity lets say in this case MainActivity like this:

TestDialog testDialog = new TestDialog(MainActivity.this);
testDialog.showDialog();

I have not tried this from a Fragment , so I do not know whether this works from a Fragment or whether you will continue to have issues with certain devices. For those reasons, I (and Google!) still strongly suggest that you use the DialogFragment instead, because is has been especially designed for this scenario. Take a look at the Google Docs.:

https://developer.android.com/guide/topics/ui/dialogs

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM