How to handle AsyncTask onPostExecute when paused to avoid IllegalStateException

后端 未结 5 741
灰色年华
灰色年华 2020-11-30 18:44

I appreciate the numerous postings regarding AsyncTask on a rotation change. I have the following problem when using the compatability lib and trying to dismiss a Dial

相关标签:
5条回答
  • 2020-11-30 19:13

    Another way of achieving what you require is to implement the PauseHandler class that I documented in this post.

    Then in your onPostExecute method call sendMessage() to post your message into the handler.

    When your application resumes the action will be handled.

    0 讨论(0)
  • 2020-11-30 19:25

    On How to handle Handler messages when activity/fragment is paused I offer another approach using a BroadcastReceiver.

    I consider it cleaner more elegant and it offers the advantages that you can invoke code on your base fragment from everywhere within your app and by using sticky broadcasts your invocation can be "remembered" and executed after your fragment resumes.

    0 讨论(0)
  • 2020-11-30 19:33

    I came up with a solution for this problem without any major workaround: The basic idea how to maintain a progressdialog and a asynctask is described in this blogentry (of course I used the AsyncTaskComplex-Version). All credits go to the author of this blogentry, I only added a tiny thing:

    Obviously I'm not using showDialog() anymore. Instead I stick with DialogFragments.

    The second tweak is the importent one and also solves the problem with the IllegalStateException:

    Instead of only telling the asynctask in onRetainCustomNonConfigurationInstance() that there is no more activity I also do it in onPause(). And instead of only telling the asynctask in onCreate() that there is a new activity I also do it in onResume().

    And there you go, your AsyncTask will not try to inform your activity about his finish causing an IllegalStateException when the activity is not visible.

    If you would like to see more code instead of words, leave a comment.

    /edit: Sourcecode to show my solution, which I think is a pretty decent one :)

    public class MyActivity extends Activity {
    
    private MyTask mTask;
    
    @Override
    protected void onCreate(Bundle pSavedInstanceState) {
        super.onCreate(pSavedInstanceState);
        setContentView(R.layout.editaccount);
    
        Object retained = getLastCustomNonConfigurationInstance();
        if ( retained instanceof NewContactFolderIdTask ) {
            mTask = (MyTask) retained;
            mTask.setActivity(this);
        }
    
    }
    @Override
    protected void onPause() {
        if(mTask != null) {
            mTask.setActivity(null);
        }
        super.onPause();
    }
    
    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        if(mTask != null) {
            mTask.setActivity(null);
            return mTask;
        }
        return null;
    }
    
    @Override
    protected void onResume() {
        if(mTask != null) {
            mTask.setActivity(this);
        }
        loadValues(); // or refreshListView or whatever you need to do
        super.onResume();
    }
    
    public void onTaskCompleted() {
        loadValues();  // or refreshListView or whatever you need to do
        DialogFragment dialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(PROGRESS_DIALOG_FRAGMENT);
        if(dialogFragment != null) {
            dialogFragment.dismiss();
        }
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                // app icon in Action Bar clicked; go home
                Intent intent = new Intent(this, OXClient.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                return true;
            case R.id.menu_refresh:
                mTask = new MyTask(this);
                mTask.execute();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
    
    
    private class NewContactFolderIdTask extends AsyncTask<Boolean, Integer, Bundle> {
        private MyActivity mActivity;
        private boolean mCompleted;
    
        private NewContactFolderIdTask(MyActivity pActivity) {
            this.mActivity = pActivity;
        }
    
        public void setActivity(MyActivity pActivity) {
            this.mActivity = pActivity;
            if(mCompleted) {
                notifiyActivityTaskCompleted();
            }
        }
    
        private void notifiyActivityTaskCompleted() {
            if(mActivity != null) {
                mActivity.onTaskCompleted();
            }
        }
    
        @Override
        protected Bundle doInBackground(Boolean... pBoolean) {
            // Do your stuff, return result
        }
    
        @Override
        protected void onPreExecute() {
            DialogFragment newFragment = ProgressDialogFragment.newInstance();
            newFragment.show(getSupportFragmentManager(), PROGRESS_DIALOG_FRAGMENT);
        }
    
        @Override
        protected void onPostExecute(Bundle pResult) {
            mCompleted = true;
            notifiyActivityTaskCompleted();
        }
    }
    

    }

    0 讨论(0)
  • 2020-11-30 19:34

    If you need to synchronize your task with the activity lifecycle, I believe that Loaders are exactly what you need. More specifically, you should use AsyncTaskLoader to do the job. So now instead of running an AsyncTask, you launch your loader, then wait for response in a listener. If the activity is paused, you won't get a callback, this part will be managed for you.

    There is another way to handle this task: using a fragment which retains its instance. The general idea is that you create a fragment without UI and call setRetainInstance(true). It has a task which is being notified about the activity being available or not. If not, the task's thread suspends until an activity becomes available.

    0 讨论(0)
  • 2020-11-30 19:38

    Rather then using BroadcastReceiver, I prefer using bus libraries like guava, otto or eventbus. Their performance is much better then the broadcast receiver implementation.

    0 讨论(0)
提交回复
热议问题