簡體   English   中英

Android AlertDialog是否因BadTokenException而崩潰?

[英]Android AlertDialog crashes with BadTokenException?

這是我的第一篇文章,請耐心等待。 我是一名高中開發人員,最近在Play商店發布了一個Android應用。 我正在使用Crashlytics捕獲異常,由於這個原因,它引發了此錯誤。

Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@1989547c is not valid; is your activity running?

專門針對LG D855,Nexus 5和Huawei PLK AL10(版本5.0、5.0.2、5.1.1和6.0.1)進行了報道。 我在網上查看,發現在不存在活動時會發生這種情況。 該錯誤在應用程序的初次啟動時發生。

以下是我用於“警報”對話框的代碼,該代碼僅詢問用戶是否要查看教程(是/否)

public void showTutorialDialog() {
    AlertDialog tutorialDialog = new AlertDialog.Builder(this)
            .setTitle(R.string.tutorial_question_title)
            .setCancelable(false)
            .setMessage(R.string.tutorial_question)
            .setPositiveButton(getResources().getString(R.string.tutorial_question_pos), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // Take to tutorial
                    // Assume isn't backer for now..
                    finish();
                    Intent i = new Intent(StartupActivity.this, TutorialActivity.class);
                    i.putExtra("from", "StartupActivity");
                    startActivity(i);
                }
            })
            .setNegativeButton(getResources().getString(R.string.tutorial_question_neg), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // No tutorial, ask if they are a backer
                    showBackerDialog();
                }
            }).show();

最初啟動應用程序時,我使用IabHelper在單獨的類中加載用戶的購買詳細信息。 此類稱為PurchaseRetriever,該類異步檢索用戶購買的內容並將其存儲在ArrayList中。 這就是我的代碼的工作方式。

            if (mManager.isUserFirstTime()) {
                // Initialize purchase retriever.
                // The rest will be done when the observer reports that purchase data has been retrieved.
                mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
                mPurchases.addObserver(new FirstStartupObserver(this));
                StartupManager.FIRST = true;
                loadImageContent();

它運行使用觀察者模式,所以當購買細節被查詢它調用在FirstStartupObserver更新()方法,該方法然后通過向StartupActivity的引用,調用startupActivity.showTutorialDialog(); 發生錯誤的位置。

我已經在我和我的朋友們自己擁有的多種設備(Nexus 6,Nexus 5,Nexus 7平板電腦,Samsung Galaxy Tab,Samsung Remote Lab上的各種設備)上對其進行了測試,但是在我的終端上仍然可以正常工作...任何建議,謝謝。

編輯:這是StartupActivity。

/**
 * Main startup activity. Determines which activity to launch.
 * Puts the user in one place or another depending on if they are a backer.
 */
public class StartupActivity extends AppCompatActivity {

    private StartupManager mManager;
    private ProgressBar bar;

    // --- Used if first time app loading to query purchase info
    private PurchaseRetriever mPurchases;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // Used in either cases
        // If first time, displayed, if not, hidden//
        //hideNavBar();
        User.init(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_startup);
        bar = (ProgressBar)this.findViewById(R.id.progressBar);
        mManager = new StartupManager(this);

        // Returns true if data was corrupt before
        if (mManager.isDataCorrupt()) {
            bar.setVisibility(View.VISIBLE);
            loadImageContent();
            // Reset watch to default black
            // Internally starts NewMainActivity
            ErrorManager.fixCorruptData(bar, this);
        } else {
            // Stays true until user selects watch
            if (mManager.isUserFirstTime()) {
                // Initialize purchase retriever.
                // The rest will be done when the observer reports that purchase data has been retrieved.
                mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
                mPurchases.addObserver(new FirstStartupObserver(this));
                StartupManager.FIRST = true;
                loadImageContent();
            } else {
                // NOT first time starting app.
                mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
                mPurchases.addObserver(new AfterFirstStartupObserver(this));
                loadImageContent();
            }
        }


    }

    // Two main dialogs used
    public void showTutorialDialog() {
        AlertDialog tutorialDialog = new AlertDialog.Builder(this)
                .setTitle(R.string.tutorial_question_title)
                .setCancelable(false)
                .setMessage(R.string.tutorial_question)
                .setPositiveButton(getResources().getString(R.string.tutorial_question_pos), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Take to tutorial
                        // Assume isn't backer for now..
                        finish();
                        Intent i = new Intent(StartupActivity.this, TutorialActivity.class);
                        i.putExtra("from", "StartupActivity");
                        startActivity(i);
                    }
                })
                .setNegativeButton(getResources().getString(R.string.tutorial_question_neg), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // No tutorial, ask if they are a backer
                        showBackerDialog();
                    }
                }).show();

        tutorialDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.RED);
        tutorialDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
    }
    private void showBackerDialog() {
        // Show AlertDialog ask if they are kickstarter backer
        AlertDialog askDialog = new AlertDialog.Builder(this)
                .setTitle(getResources().getString(R.string.startup_dialog_title))
                .setCancelable(false)
                .setMessage(getResources().getString(R.string.startup_dialog_message))
                .setPositiveButton(getResources().getString(R.string.startup_dialog_pos), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // User is a backer, take to watch chooser screen, then it takes to login screen
                        // Also look at Timer with TimerTask
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Intent i = new Intent(StartupActivity.this, WatchChooserActivity.class);
                                    i.putExtra("from", "StartupActivityBacker");
                                    startActivity(i);
                                } finally {
                                    finish();
                                }
                            }
                        }).start();
                    }
                })
                .setNegativeButton(getResources().getString(R.string.startup_dialog_neg), new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // User is not a backer, take to MainActivity

                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                try {
                                    Intent i = new Intent(StartupActivity.this, WatchChooserActivity.class);
                                    i.putExtra("from", "StartupActivityNonBacker");
                                    startActivity(i);
                                } finally {
                                    finish();
                                }
                            }
                        }).start();
                    }
                }).show();

        askDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.RED);
        askDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
    }

這是FirstStartupObserver的代碼。

public class FirstStartupObserver implements Observer {
    private StartupActivity startupActivity;
    public FirstStartupObserver(StartupActivity startupActivity) {
        this.startupActivity = startupActivity;
    }

    // Called when the observable is done loading purchase detail
    // (Only called when user runs app first time)
    @Override
    public void update(Observable observable, Object data) {
        // Set default first-time watch
        //  Query product data (the Watchfaces purchased in the form of a WatchFace object)
        PurchaseRetriever mPurchases = PurchaseRetriever.getInstance(startupActivity);
        if (mPurchases.hasSuccess()) {
            ArrayList<DynamicLoader.WatchFace> facesOwned = mPurchases.getPurchasedFaces();
            for (DynamicLoader.WatchFace f : facesOwned) {
                f.setPurchased(true);
            }
            // Check if coming from v1.4
            if (UpgradeManager.isUpgrading(startupActivity)) {
                // Then it calls the code below, but after the async task.
                String accessCode = UpgradeManager.getOldAccessCode(startupActivity);
                String accessToken = UpgradeManager.getOldAccessToken(startupActivity);
                UpgradeManager.migrateBacker(startupActivity, accessCode, accessToken);
            } else {
                // Ask if they want to see tutorial.
                // This is when the exception occurs!!!

                startupActivity.showTutorialDialog();

            }
            return;
        } else {
            Log.d("TAG", "Showing fail dialog");
            DialogUtils.showIabFailDialog(startupActivity, this);
        }
    }
}

令牌android.os.BinderProxy@1989547c無效; 您的活動正在進行嗎?

您嘗試在活動不存在時過早加載AlertDialog 在活動生命周期完成后,我會在我的應用程序中加載一些教程:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splash_screen);

    ...
    ...
    ...

    showTutorialDialog();
}

令牌android.os.BinderProxy@1989547c無效; 您的活動正在進行嗎?

這意味着您正在嘗試在活動被銷毀時或銷毀之后顯示彈出窗口。

您可以檢查您的活動是否被isDestroyed如下所示:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && !isDestroyed()) {
    showTutorialDialog();
}

如果您支持api 17以下設備,則可以嘗試使用isFinishing 我沒有測試它是否按預期工作。 (如果我錯了,請糾正我。)

else {
        if (!isFinishing()) { 
             showTutorialDialog();
         }
   }

或快速解決,您可以try catch

這通常是由於在AsyncTask或其他后台任務中執行某些操作而引起的,這些任務持有對Activity的引用,並在工作完成后嘗試顯示對話框。 在這種情況下,聽起來您的FirstStartupObserver持有該活動的引用並試圖顯示一個對話框,但是在PurchaseRetriever完成其工作時,該活動可能已被破壞。

不要嘗試測試活動狀態,也不要捕獲BadTokenException 那只是掩蓋了問題。 最簡單的解決方案是在活動暫停時取消PurchaseRetriever 如果您希望后台工作在輪換之類的配置更改中幸存下來,但仍限於用戶認為的活動生命周期,請在保留的片段中進行工作。 最后,如果當用戶在活動之間導航或將應用置於后台時后台工作應繼續進行,則可以在Service進行工作並將結果保存在活動可以檢索的位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM