进度对话框和后台线程处于活动状态时如何处理屏幕方向更改?

我的程序在后台线程中执行一些网络活动。 在开始之前,它弹出一个进度对话框。 对话框在处理程序中被解除。 这一切都可以正常工作,除非在对话框启动后屏幕方向发生变化(并且后台线程正在运行)。 此时,应用程序崩溃或死锁,或进入一个奇怪的阶段,直到所有线程都被杀死,应用程序根本无法工作。

我如何优雅地处理屏幕方向变化?

下面的示例代码大概匹配我的真实程序的功能:

public class MyAct extends Activity implements Runnable {
    public ProgressDialog mProgress;

    // UI has a button that when pressed calls send

    public void send() {
         mProgress = ProgressDialog.show(this, "Please wait", 
                      "Please wait", 
                      true, true);
        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        Thread.sleep(10000);
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mProgress.dismiss();
        }
    };
}

堆栈:

E/WindowManager(  244): Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244):     at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager(  244):     at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager(  244):     at android.app.Dialog.show(Dialog.java:212)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager(  244):     at MyAct.send(MyAct.java:294)
E/WindowManager(  244):     at MyAct$4.onClick(MyAct.java:174)
E/WindowManager(  244):     at android.view.View.performClick(View.java:2129)
E/WindowManager(  244):     at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager(  244):     at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager(  244):     at android.view.View.dispatchTouchEvent(View.java:3198)

我试图关闭onSaveInstanceState中的进度对话框,但这只是防止立即崩溃。 后台线程仍在继续,并且UI处于部分绘制状态。 在重新开始工作之前需要杀死整个应用程序。


当您切换方向时,Android会创建一个新的视图。 你可能会因为后台线程试图改变旧版本的状态而崩溃。 (它可能也有麻烦,因为你的后台线程不在UI线程上)

我建议让这个mHandler不稳定,并在方向改变时更新它。


编辑: Google工程师不推荐这种方法,正如Dianne Hackborn(又名hackbod)在这篇StackOverflow文章中所描述的 。 查看这篇博文了解更多信息。


您必须将其添加到清单中的活动声明中:

android:configChanges="orientation|screenSize"

所以它看起来像

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

问题在于当配置发生更改时,系统会破坏活动。 请参阅ConfigurationChanges。

所以把它放在配置文件中可以避免系统破坏你的活动。 相反,它会调用onConfigurationChanged(Configuration)方法。


我为这些符合'Android Way'事件的问题提出了一个坚如磐石的解决方案。 我有使用IntentService模式的所有长时间运行的操作。
这是我的活动广播意图,IntentService完成工作,将数据保存在数据库中,然后广播STICKY意图。
粘性部分非常重要,即使在用户启动工作之后的时间内活动暂停,并且错过了ItentService的实时广播,我们仍然可以响应并从调用活动中获取数据。
ProgressDialogs可以用onSaveInstanceSate()很好地工作到这种模式。
基本上,您需要保存一个标志,在已保存的实例包中运行进度对话框。 不要保存进度对话框对象,因为这会泄漏整个活动。
要获得进度对话框的持久句柄,我将它作为应用程序对象中的弱引用存储。
在方向改变或导致活动暂停(电话呼叫,用户点击回家等)然后恢复的其他任何事情时,我会关闭旧对话框并在新创建的活动中重新创建一个新对话框。
对于无限期的进度对话框,这很容易。 对于进度条风格,您必须将捆绑中最后一个已知的进度以及您在活动中本地使用的任何信息记录到进度中。
在恢复进度时,您将使用此信息重新生成与之前相同状态的进度栏,然后根据事物的当前状态进行更新。
总而言之,将长时间运行的任务放入IntentService中,再配合使用onSaveInstanceState()可以有效地跟踪对话并在整个活动生命周期事件中进行恢复。 下面是Activity代码的相关位。 您还需要您的BroadcastReceiver中的逻辑来正确处理粘滞意图,但这超出了范围。

   public void doSignIn(View view) {
        waiting=true;
        AppClass app=(AppClass) getApplication();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
..}


@Override
    protected void onSaveInstanceState(Bundle saveState) {
        super.onSaveInstanceState(saveState);
        saveState.putBoolean("waiting",waiting);
    }

    @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if(savedInstanceState!=null) {
                restoreProgress(savedInstanceState);

            }
...}


private void restoreProgress(Bundle savedInstanceState) {
        waiting=savedInstanceState.getBoolean("waiting");
        if (waiting) {
            AppClass app=(AppClass) getApplication();
            ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
            refresher.dismiss();
            String logingon=getString(R.string.signon);
            app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));



        }

    }
链接地址: http://www.djcxy.com/p/19929.html

上一篇: How to handle screen orientation change when progress dialog and background thread active?

下一篇: Memory Leaks not detected by CRT Memory Leak Detection