【问题标题】:How to use a ProgressDialog properly with an AsyncTask without causing window leaks?如何在 AsyncTask 中正确使用 ProgressDialog 而不会导致窗口泄漏?
【发布时间】:2013-11-19 15:07:56
【问题描述】:

我有一个显示 ProgressDialog 的 AsyncTask。 AsyncTask 在 Activity 启动时启动:

public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_layout);
        new MyTask().execute();
    }

    // ... other code

    private class MyTask extends AsyncTask<Void, Void, Void>{
        private ProgressDialog dialog = new ProgressDialog(MyActivity.this);
        @Override
        protected void onPreExecute() {
            dialog.show();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            // get data from a server
            return null;
        }    

        @Override
        protected void onPostExecute(Void aVoid) {
            // call to a method in MyActivity which updates the UI.
            if (dialog.isShowing()) {
                dialog.dismiss();
            }
        }
    }
}

此代码运行良好,直到我旋转屏幕。这是有道理的,因为用于创建对话框的上下文不再存在(因为旋转时会重新创建活动),并导致窗口泄漏。

我能想到的唯一解决方案不是一个很好的解决方案:创建任务和对话框的静态实例,并在活动被销毁时简单地关闭对话框,如果任务完成,则在 oncreate 方法中重新创建对话框仍在运行。

那么我如何在不丢失功能的情况下解决这样的问题(因此在任务运行时必须始终显示对话框,并且应该允许旋转设备)?

【问题讨论】:

标签: java android android-asynctask progressdialog


【解决方案1】:

正如 Raghunandan 在他的评论中建议的那样,我研究了 Fragments 并解决了我的问题。

我创建了一个 Fragment 来启动我的 AsyncTask,如 Raghunandan 提供的 blogpost 中所述。

为了确保我的 Dialog 没有泄露,我创建了一个 DialogFragment,如 here(基本对话框)所述。

这是我的工作代码:

我的活动:

public class MyActivity extends Activity implements MyTaskFragment.TaskCallbacks {
    private MyTaskFragment task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_layout);

        FragmentManager fm = getFragmentManager();
        task = (MyTaskFragment) fm.findFragmentByTag("myTask");

        if (task == null) {
            task = new MyTaskFragment();
            fm.beginTransaction().add(task, "myTask").commit();
        }
    }

    @Override
    public void onPreExecute() {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        Fragment prev = getFragmentManager().findFragmentByTag("myDialog");
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack(null);

        StringProgressDialogFragment dialog = StringProgressDialogFragment.newInstance("My message");
        dialog.show(ft, "myDialog");
    }

    @Override
    public void onPostExecute() {
        StringProgressDialogFragment dialog = (StringProgressDialogFragment) getFragmentManager().findFragmentByTag("myDialog");
        if (dialog!=null) {
            dialog.dismiss();
        }
        // update UI
    }

    // ... other code
}

我的任务片段:

public class MyTaskFragment extends Fragment {
    private TaskCallbacks mCallbacks;
    private Task mTask;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mCallbacks = (TaskCallbacks) activity;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retain this fragment across configuration changes.
        setRetainInstance(true);

        // Create and execute the background task.
        mTask = new Task();
        mTask.execute();
    }

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

    private class Task extends AsyncTask<Void, Void, Void> {
        @Override
        protected void onPreExecute() {
            mCallbacks.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            // do stuff
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            mCallbacks.onPostExecute();
        }
    }

    public static interface TaskCallbacks {
        void onPreExecute();
        void onPostExecute();
    }
}

我的对话框片段:

public class StringProgressDialogFragment extends DialogFragment {
    private String message;

    public static StringProgressDialogFragment newInstance(String message) {
        StringProgressDialogFragment dialog = new StringProgressDialogFragment();
        Bundle args = new Bundle();
        args.putString("message", message);
        dialog.setArguments(args);
        return dialog;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ProgressDialog dialog = new ProgressDialog(getActivity());
        message = getArguments().getString("message");
        dialog.setMessage(message);
        return dialog;
    }
}

【讨论】:

    【解决方案2】:

    新的 Loaders API 可以帮助您(通过支持包提供)-man。他们将解决旋转问题,但不是内存。泄漏。解决mem。泄漏编写您自己的“AsyncTask”(使用“clearContext”例程)并在活动的 onDestroy(或 onPause,取决于您的架构)中清除它的上下文。它可能看起来像一辆自行车,但该任务最多需要 1 天,并且您将完全控制后台工作人员使用的所有资源。

    顺便说一句:考虑通过片段使用对话框,因为它解决了屏幕旋转时的对话框杀死问题。

    【讨论】:

    • 自定义 AsyncTask,也可以由 Aysnctask 源组成 - 它们不是那么大。
    • 这不是内存。泄漏,但是 android.view.WindowLeaked 异常。
    【解决方案3】:

    尝试样品。它会起作用的。基本上只是通过处理配置更改来限制 oncreate 调用。此解决方案可能会对您有所帮助。

    public class MainActivity extends Activity  {
    
    
    LoadProgrssdata task = new LoadProgrssdata();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
            Toast.makeText(this, "oncreate called", Toast.LENGTH_SHORT).show();
            task.execute();
    }
    
    public class LoadProgrssdata extends AsyncTask<Void, Void, Void> {
        ProgressDialog progressDialog;
        //declare other objects as per your need
        @Override
        protected void onPreExecute()
        {
            progressDialog= ProgressDialog.show(MainActivity.this, "Progress Dialog Title Text","Process Description Text", true);
    
            //do initialization of required objects objects here                
        };      
        @Override
        protected Void doInBackground(Void... params)
        {   
    
             //do loading operation here  
    
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return null;
        }       
        @Override
        protected void onPostExecute(Void result)
        {
            super.onPostExecute(result);
            progressDialog.dismiss();
        };
     }
    
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    
        // Checks the orientation of the screen
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
            Log.e("orientation ", "landscape");
    
        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
    
            Log.e("orientation ", "portrait");
        }
    }
     }
    

    在 android 清单文件中:

    <activity
            android:name="com.example.MainActivity"
            android:label="@string/app_name"  
           android:configChanges="orientation|keyboardHidden|screenSize" />
    

    【讨论】:

      【解决方案4】:

      我设法通过在 doInBackground 中捕获任何可能发生的崩溃来解决此问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-30
        • 2011-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-09
        • 2017-01-31
        • 1970-01-01
        相关资源
        最近更新 更多