Last active
October 3, 2017 08:01
-
-
Save noaht11/b15911f6a17f5ebabb44 to your computer and use it in GitHub Desktop.
Using a retained fragment to handle AsyncTasks across orientation changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NetworkRequestFragment extends Fragment { | |
// Declare some sort of interface that your AsyncTask will use to communicate with the Activity | |
public interface NetworkRequestListener { | |
void onRequestStarted(); | |
void onRequestProgressUpdate(int progress); | |
void onRequestFinished(SomeObject result); | |
} | |
private NetworkTask mTask; | |
private NetworkRequestListener mListener; | |
private SomeObject mResult; | |
@Override | |
public void onAttach(Activity activity) { | |
super.onAttach(activity); | |
// Try to use the Activity as a listener | |
if (activity instanceof NetworkRequestListener) { | |
mListener = (NetworkRequestListener) activity; | |
} else { | |
// You can decide if you want to mandate that the Activity implements your callback interface | |
// in which case you should throw an exception if it doesn't: | |
throw new IllegalStateException("Parent activity must implement NetworkRequestListener"); | |
// or you could just swallow it and allow a state where nobody is listening | |
} | |
} | |
@Override | |
public void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
// Retain this Fragment so that it will not be destroyed when an orientation | |
// change happens and we can keep our AsyncTask running | |
setRetainInstance(true); | |
} | |
/** | |
* The Activity can call this when it wants to start the task | |
*/ | |
public void startTask(String url) { | |
mTask = new NetworkTask(url); | |
mTask.execute(); | |
} | |
@Override | |
public void onActivityCreated(Bundle savedInstanceState) { | |
super.onActivityCreated(savedInstanceState); | |
// If the AsyncTask finished when we didn't have a listener we can | |
// deliver the result here | |
if ((mResult != null) && (mListener != null)) { | |
mListener.onRequestFinished(mResult); | |
mResult = null; | |
} | |
} | |
@Override | |
public void onDestroy() { | |
super.onDestroy(); | |
// We still have to cancel the task in onDestroy because if the user exits the app or | |
// finishes the Activity, we don't want the task to keep running | |
// Since we are retaining the Fragment, onDestroy won't be called for an orientation change | |
// so this won't affect our ability to keep the task running when the user rotates the device | |
if ((mTask != null) && (mTask.getStatus == AsyncTask.Status.RUNNING)) { | |
mTask.cancel(true); | |
} | |
} | |
@Override | |
public void onDetach() { | |
super.onDetach(); | |
// This is VERY important to avoid a memory leak (because mListener is really a reference to an Activity) | |
// When the orientation change occurs, onDetach will be called and since the Activity is being destroyed | |
// we don't want to keep any references to it | |
// When the Activity is being re-created, onAttach will be called and we will get our listener back | |
mListener = null; | |
} | |
private class NetworkTask extends AsyncTask<String, Integer, SomeObject> { | |
@Override | |
protected void onPreExecute() { | |
if (mListener != null) { | |
mListener.onRequestStarted(); | |
} | |
} | |
@Override | |
protected SomeObject doInBackground(String... urls) { | |
// Make the network request | |
... | |
// Whenever we want to update our progress: | |
publishProgress(progress); | |
... | |
return result; | |
} | |
@Override | |
protected void onProgressUpdate(Integer... progress) { | |
if (mListener != null) { | |
mListener.onRequestProgressUpdate(progress[0]); | |
} | |
} | |
@Override | |
protected void onPostExecute(SomeObject result) { | |
if (mListener != null) { | |
mListener.onRequestFinished(result); | |
} else { | |
// If the task finishes while the orientation change is happening and while | |
// the Fragment is not attached to an Activity, our mListener might be null | |
// If you need to make sure that the result eventually gets to the Activity | |
// you could save the result here, then in onActivityCreated you can pass it back | |
// to the Activity | |
mResult = result; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment