我的程序在后台线程中执行一些网络活动。在开始之前,它会弹出一个进度对话框。该对话框在处理程序上被解除。这一切都很好,除了当对话框打开时屏幕方向发生变化(背景线程正在运行)。此时,应用程序要么崩溃,要么死锁,要么进入一个奇怪的阶段,在所有线程被杀死之前,应用程序根本无法工作。

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

下面的示例代码大致匹配我的实际程序:

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处于部分绘制状态。需要在它重新开始工作之前杀死整个应用程序。


当前回答

这是一个非常古老的问题,出于某种原因出现在侧栏上。

如果后台任务只需要在活动处于前台时存活,“新的”解决方案是在保留的片段中托管后台线程(或者,最好是AsyncTask),如本开发指南和大量q&a中所述。

如果活动因配置更改而被销毁,则保留的片段将继续存在,但当活动在后台或后台堆栈中被销毁时则不存在。因此,如果onPause()中的isChangingConfigurations()为false,后台任务仍然应该被中断。

其他回答

将长任务移动到单独的类中。将其实现为主题-观察者模式。每当创建活动时,注册并在关闭时注销任务类。任务类可以使用AsyncTask。

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


你必须把这个添加到manifest中的activity声明中:

android:configChanges="orientation|screenSize"

看起来是这样的

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

问题是,当配置发生更改时,系统将破坏活动。看到ConfigurationChanges。

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

如果你创建了一个后台服务来完成所有繁重的工作(tcp请求/响应,解组),视图和活动可以被销毁并重新创建,而不会泄漏窗口或丢失数据。这允许Android推荐的行为,即在每次配置更改时销毁一个Activity。对于每个方向变化)。

它有点复杂,但它是调用服务器请求、数据预处理/后处理等的最佳方式。

您甚至可以使用您的服务将每个请求排队到服务器,因此它使处理这些事情变得简单和高效。

开发指南有一个关于服务的完整章节。

我是一个新手在机器人,我尝试了这个,它是有效的。

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}

我的解决方案是扩展ProgressDialog类来获得我自己的MyProgressDialog。

我重新定义了show()和dismiss()方法来在显示对话框之前锁定方向,并在对话框被解散时将其解锁。

因此,当显示对话框并且设备的方向发生变化时,屏幕的方向保持不变,直到调用dismiss(),然后屏幕方向根据传感器值/设备方向发生变化。

这是我的代码:

public class MyProgressDialog extends ProgressDialog {
    private Context mContext;

    public MyProgressDialog(Context context) {
        super(context);
        mContext = context;
    }

    public MyProgressDialog(Context context, int theme) {
        super(context, theme);
        mContext = context;
    }
    
    public void show() {
        if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
            ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        else
            ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        super.show();
    }
    
    public void dismiss() {
        super.dismiss();
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }
}