简体   繁体   English

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

[英]How to use a ProgressDialog properly with an AsyncTask without causing window leaks?

I have an AsyncTask which shows a ProgressDialog. 我有一个AsyncTask,它显示一个ProgressDialog。 The AsyncTask is started when the activity is started: 当活动开始时,将启动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();
            }
        }
    }
}

This code works perfectly, untill I rotate my screen. 直到我旋转屏幕,这段代码才能正常工作。 Which makes sense, because the context that was used to create the dialog doesn't exist anymore (because the activity is re-created when rotating), and a window leak is caused. 这是有道理的,因为用于创建对话框的上下文已不存在(因为在旋转时会重新创建活动),并且会导致窗口泄漏。

The only solution I could think of isn't a really nice one: create a static instance of the task and dialog, and simply dismiss the dialog when the activity is destroyed, and recreate the dialog in the oncreate method if the task is still running. 我能想到的唯一解决方案不是一个很好的解决方案:创建任务和对话框的静态实例,并在活动被破坏时简单地关闭对话框,如果任务仍在运行,则使用oncreate方法重新创建对话框。

So how would I solve something like this without losing functionality (so the dialog must always be shown when the task is running, and rotating the device should be allowed)? 那么,如何解决这种问题而不丢失功能(因此在任务运行时必须始终显示对话框,并且应该允许旋转设备)?

As Raghunandan suggested in his comment, I looked into Fragments and solved my problem. 正如Raghunandan在评论中建议的那样,我调查了Fragments并解决了我的问题。

I created a Fragment which starts my AsyncTask, as explained in the blogpost that Raghunandan provided. 正如Raghunandan提供的博客文章所述 ,我创建了一个片段来启动AsyncTask。

And to make sure that my Dialog didn't get leaked, I created a DialogFragment, as described here (Basic Dialog). 而要确保我的对话框没有得到泄露,我创建了一个DialogFragment,描述在这里 (基本对话)。

Here's my working code: 这是我的工作代码:

My Activity: 我的活动:

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
}

My Task fragment: 我的任务片段:

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

My Dialog fragment: 我的对话框片段:

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

try with sample. 尝试样品。 it will work. 它会工作。 basically just restrict the oncreate call by handling the config change. 基本上只是通过处理配置更改来限制oncreate调用。 this solution may help you. 此解决方案可能会对您有所帮助。

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

and in android manifest file: 并在android清单文件中:

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

New Loaders API can help you (available via support package) - man . 新的Loaders API可以为您提供帮助(可通过支持包获得) -man They will solve problem with rotation, but not a mem. 他们将解决轮换问题,但不会解决问题。 leak. 泄漏。 To solve mem. 解决内存问题。 leaks write your own "AsyncTask" (with a "clearContext" routine) and clear it's context in activity's onDestroy (or onPause, depends on your architecture). 泄漏编写您自己的“ AsyncTask”(带有“ clearContext”例程),并在活动的onDestroy(或onPause,取决于您的体系结构)中清除其上下文。 It may looks like a bicycle, but the task takes max 1 day, and you will have a full control on all the resources you background worker use. 它可能看起来像一辆自行车,但是该任务最多需要1天的时间,并且您将完全控制后台工作人员使用的所有资源。

By the way: consider using dialogs through fragments, because it solves dialog kill on screen rotation. 顺便说一句:考虑通过片段使用对话框,因为它解决了屏幕旋转时对话框被杀死的问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM