Search code examples
androidandroid-asynctaskandroid-dialogfragmentandroid-orientationandroid-configchanges

OrientationChange handling Activity, Fragment, AsyncTask and DialogFragments?


Hi there I'm thinking about what is the correct and best way to handle Activity, Fragment, AsyncTask and DialogFragments together.

  • My current state is that I start my Activity and replace its ContentView with my Fragment, in which I got an EditText and one Button.

  • Tapping my Button executes an AsyncTasks which Requests random things and takes some time. Meanwhile I display a DialogFragment begging for patience.

  • Desired behavior is that, e.g. I rotate my screen my DialogFragment keeps being displayed for the time my AsyncTask is running. After that I want to show up a simple toast displaying the information I got from my HttpRequest.

Compact overview about how I thought it would work:

  • BaseFragment keeps a WeakReference to the Activity it's attached to
  • AsyncTask keeps a WeakReference to Fragment which exectures it
  • AsyncTasks onPreExecute() shows up the DialogFragment
  • AsyncTasks onPostExecute() dissmisses the DialogFragment
  • BaseFragment holds DialogFragment

Unfortunately this is not the way it works, on orientation change my DialogFragment keeps being displayed and no toast is showing up. What am I doing wrong ?

public class BaseFragment extends Fragment{
private static final String TAG = BaseFragment.class.getSimpleName();

protected WeakReference<AppCompatActivity> mActivity;
private TemplateDialogFragment dialogFragment;

public WeakReference<AppCompatActivity> getAppCompatActivity(){ return mActivity; }

@Override
public void onAttach(Context context) {
    if(!(context instanceof AppCompatActivity)) {
        throw new IllegalStateException(TAG + " is not attached to an AppCompatActivity.");
    }

    mActivity = new WeakReference<>((AppCompatActivity) context);

    super.onAttach(context);
}

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

@Override
public void onStart() {
    super.onStart();
    showContent();
}

public void showContent(){

}

public void showDialog(String title, String content){
    dialogFragment = new TemplateDialogFragment();

    Bundle bundle = new Bundle();
    bundle.putString(TemplateDialogFragment.DIALOG_TITLE, title);
    bundle.putString(TemplateDialogFragment.DIALOG_MESSAGE, content);

    dialogFragment.setArguments(bundle);
    dialogFragment.show(getFragmentManager(), TemplateDialogFragment.FRAGMENT_TAG);
}

public void notifyTaskFinished(String result) {
    dismissDialog();

    if(mActivity != null && !mActivity.get().isFinishing()) {
        Toast.makeText(mActivity.get().getApplicationContext(), result, Toast.LENGTH_LONG).show();
    }
}

private void dismissDialog(){
    if(dialogFragment != null && dialogFragment.isAdded()) {
        dialogFragment.dismissAllowingStateLoss();
    }
}

}

...

public class TemplateFragment extends BaseFragment {
private static final String TAG = TemplateFragment.class.getSimpleName();

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.test_fragment, container, false);
}
@Override
public void showContent() {
    super.showContent();

    Button startTask = (Button) getAppCompatActivity().get().findViewById(R.id.button0);
    final BaseFragment instance = this;

    startTask.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            CustomAsyncTask task = new CustomAsyncTask(instance);
            EditText input = (EditText) getAppCompatActivity().get().findViewById(R.id.text0);
            task.execute(input.getText().toString());
        }
    });
}

private static class CustomAsyncTask extends AsyncTask<String, Void, String> {
    WeakReference<BaseFragment> weakBaseFragmentReference;

    private CustomAsyncTask(BaseFragment fragment) {
        weakBaseFragmentReference = new WeakReference<>(fragment);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        weakBaseFragmentReference.get().showDialog("Executing", "Working on the request...");
    }

    @Override
    protected String doInBackground(String... params) {
        HttpURLConnection con = HttpUrlConnectionFactory.createUrlConnection("https://www.httpbin.org/bytes/" + (params[0] == null ? "1" : params[0]));
        return HttpRequester.doGet(con).getResponseAsString();
    }

    @Override
    protected void onPostExecute(String response) {
        super.onPostExecute(response);
        if(weakBaseFragmentReference.get() == null) {
            return;
        }
        weakBaseFragmentReference.get().notifyTaskFinished(response);
    }
}

}

*Edit:

After some time researching this theme I'm sure a Service is the best solution for most of my field of use. Also I used AsyncTaskLoaders a lot, because there is a smooth control of lifecycle....


Solution

  • Use progress bar instead of DialogFragment.

    AsyncTask should only be used for tasks that take quite few seconds.

    AsyncTask doesn't respect Activity lifecycle, and can lead to memory leaks.

    Check some gotchas.

    You can try AsyncTaskLoader to survive configuration changes.