AlertDialog show()给出WindowManager BadTokenException

时间:2018-02-14 16:41:18

标签: android dialog alertdialog android-alertdialog android-dialog

我在google play开发者控制台上收到了很多关于该例外的报告,我无法理解为什么,因为我无法重现错误,它在我的所有设备上都能正常运行

这是我的自定义AlertDialog的源代码,崩溃的行是在方法结束时的show()调用上。有什么问题?

我在stackoverflow中检查了一些与此相关的问题,但我在宣布对话时实际上使用final,这是其他问题的解决方案,而且我还有异常报告。< / p>

非常感谢

public static void showPoliciesDialog(final Activity activity, final Runnable runnable) {
    int sw = App.getInstance().getSmallSideSize();
    final int LIGHT_GRAY = 0xFFc7c7c7;

    final AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(activity, R.style.PolicyStyle));
    builder.setCancelable(false);

    LinearLayout ll = new LinearLayout(activity);
    ll.setPadding((int)(sw*0.025), 0, (int)(sw*0.025), 0);
    ll.setOrientation(LinearLayout.VERTICAL);

    final TextView title = new TextView(activity);
    LinearLayout.LayoutParams titleLP = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT );
    titleLP.setMargins( Util.dpToPx(activity, 15), Util.dpToPx(activity, 15), Util.dpToPx(activity, 15), Util.dpToPx(activity, 15) );
    title.setLayoutParams(titleLP );
    title.setTextSize(18);
    title.setText(R.string.POLICY_TITLE);
    title.setTextColor(0xFF307282); // Blue color
    ll.addView(title);

    final CheckBox acceptCheckbox = new CheckBox(activity);
    acceptCheckbox.setTextColor(LIGHT_GRAY);
    acceptCheckbox.setText(R.string.I_WANT_DATA_COLLECT);
    acceptCheckbox.setLayoutParams( new ViewGroup.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) );
    acceptCheckbox.setVisibility(View.GONE);
    acceptCheckbox.setChecked(App.getInstance().retrieveBooleanValue(Constants.SEND_DATA_CHECKBOX, ConfigManager.getInstance().defaultStorable()));
    acceptCheckbox.setOnCheckedChangeListener(
        new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
                App.getInstance().storeBooleanValue(Constants.SEND_DATA_CHECKBOX, isChecked);
            }
        }
    );
    acceptCheckbox.setPadding(5,5,5,5);

    // Detects if the end of the web page has been reached & in this case it shows the accept checkbox.
    // Based on http://stackoverflow.com/questions/10956443/android-making-a-button-visible-once-webview-is-done-scrolling
    // And on
    // [1] - http://stackoverflow.com/questions/10794647/detect-if-webview-scroll-reach-the-end
    // [2] - (To avoid use of an deprecated method) http://stackoverflow.com/questions/16079863/how-get-webview-scale-in-android-4
    final WebView policiesWebView = new WebView(activity){
        float currentScale = -1;
        {
            this.setWebViewClient(new WebViewClient(){
                @Override public void onScaleChanged(WebView view, float oldScale, float newScale) {
                    super.onScaleChanged(view, oldScale, newScale);
                    currentScale = newScale;
                }
            });
            this.getSettings().setJavaScriptEnabled(true);
        }
        @Override public void onScrollChanged(int l, int t, int oldl, int oldt) {
            // Only called on Android versions where onScaleChanged is not called
            if(currentScale == -1)
                currentScale = this.getScale();
            int height = (int) Math.floor(this.getContentHeight() * currentScale);
            int webViewHeight = this.getHeight();
            int cutoff = height - webViewHeight - 10; // Don't be too strict on the cutoff point
            if (t >= cutoff) {
                // We need to know if it's necessary to show the accept checkbox. It should be visible only if it's not the first time that this dialog has been showed, so only
                // if the policies have been accepted previously. if we have the key stored, then, it's not the first time this dialog is being showed, so we must show the accept checkbox
                if(App.getInstance().containsKey(Constants.PRIVACY_POLICIES_ACCEPTED)){
                    acceptCheckbox.setVisibility(View.VISIBLE);
                }
            }
        }
    };

    policiesWebView.loadUrl(ConfigManager.getInstance().getPrivacyUrl());
    LinearLayout.LayoutParams policiesWebViewLayoutParams =  new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,0);
    policiesWebViewLayoutParams.weight=1;
    policiesWebView.setLayoutParams(policiesWebViewLayoutParams);
    policiesWebView.setVisibility(View.GONE);
    ll.addView(policiesWebView);
    ll.addView(acceptCheckbox);

    final TextView readPolicies = new TextView(activity);
    LinearLayout.LayoutParams readPoliciesLP = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    readPoliciesLP.setMargins( Util.dpToPx(activity, 15), 0, Util.dpToPx(activity, 10), Util.dpToPx(activity, 30) );
    readPolicies.setPadding(0,0,0,Util.dpToPx(activity, 20));
    readPolicies.setTextColor(LIGHT_GRAY);
    readPolicies.setLayoutParams(readPoliciesLP);
    SpannableString content = new SpannableString(activity.getString(R.string.PLEASE_READ_POLICIES));
    content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
    readPolicies.setText(content);
    readPolicies.setTextSize(16);
    readPolicies.setGravity(Gravity.LEFT);
    readPolicies.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View view) {
            title.setVisibility(View.GONE);
            policiesWebView.setVisibility(View.VISIBLE);
            readPolicies.setVisibility(View.GONE);
        }
    });
    ll.addView(readPolicies);

    // We need to know if we should treat this buttonas an agree button or a back button.
    // Agree mode: If it's the first time this dialog is being showed. Policies has not been accepted previously.
    // Back mode: If it's not the first time. The policies has been accepted previously.
    String buttonText = App.getInstance().containsKey(Constants.PRIVACY_POLICIES_ACCEPTED) == false ? activity.getString(R.string.AGREE) : activity.getString(R.string.BACK);

    builder.setPositiveButton(buttonText,
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                if(App.getInstance().containsKey(Constants.PRIVACY_POLICIES_ACCEPTED) == false) { //if agree mode
                    App.getInstance().storeBooleanValue(Constants.SEND_DATA_CHECKBOX, true);
                    App.getInstance().storeBooleanValue(Constants.PRIVACY_POLICIES_ACCEPTED, true);
                }

                dialogInterface.dismiss();
                if(runnable != null)
                    runnable.run();
            }
        }
    );

    // We will show close app button only if it's the first time we show this dialog.
    if(App.getInstance().containsKey(Constants.PRIVACY_POLICIES_ACCEPTED) == false)
        builder.setNegativeButton(R.string.CLOSE_APP,
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {

                    //launching navigator with non accept policies text
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    String url = "https://myurl.com/no-terms.php?idApp={appId}";
                    url=GlobalVariablesManager.getInstance().replaceValues(url, false, false);
                    intent.setData(Uri.parse(URLParser.parse(url)));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    activity.startActivity(intent);

                    //App.getInstance().storeBooleanValue(Constants.PRIVACY_POLICIES_ACCEPTED, false);
                    SectionManager.getInstance().exit(false);
                    App.getInstance().clean();
                    activity.finish();
                }
            }
        );

    builder.setView(ll);
    final AlertDialog dialog = builder.create();
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);

    // This will set the color of negative button to a gray color
    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface arg0) {
            Button buttonNegative = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
            buttonNegative.setTextColor(LIGHT_GRAY);
        }
    });

    dialog.show();
}

2 个答案:

答案 0 :(得分:2)

自己有这个错误了一会儿。以下是我从Crashlytics获得的有用见解:

&#34;此崩溃通常是由您的应用尝试使用以前完成的活动作为上下文显示对话框引起的。例如,如果一个Activity触发AsyncTask尝试在完成时显示一个对话框,但是用户在任务完成之前从Activity导航回来,就会发生这种情况。&#34;

Crashlytics建议链接:
Android – Displaying Dialogs From Background Threads
Error : BinderProxy@45d459c0 is not valid; is your activity running?

答案 1 :(得分:0)

来自BadTokenException的文档:

<source>

这个令牌被赋予了它的attach方法上的活动,所以我猜你在某些情况下试图过快地显示对话框。尝试在post块中发布show方法