如何使用AsyncTask正确使用ProgressDialog而不会导致窗口泄漏?

时间:2013-11-19 15:07:57

标签: java android android-asynctask progressdialog

我有一个显示ProgressDialog的AsyncTask。 AsyncTask在活动开始时启动:

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();
            }
        }
    }
}

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

我能想到的唯一解决方案不是一个非常好的解决方案:创建任务和对话框的静态实例,并在销毁活动时简单地关闭对话框,并在任务中重新创建对话框还在运行。

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

4 个答案:

答案 0 :(得分:1)

正如Raghunandan在评论中所说,我查看了碎片并解决了我的问题。

我创建了一个片段来启动我的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;
    }
}

答案 1 :(得分:0)

试试样品。它会工作。基本上只是通过处理配置更改来限制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" />

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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