Reputation: 19844
I am trying to refactor my fragment-heavy code with safer async tasks as I had noticed some occasional crashes. Now I am slightly better educated after some searches I've noticed my main problem is accessing activity or fragment variables from within the async task itself.
I am separating each async task into single classes, which implement interface callbacks to the fragment or activity which called them. For example:
public class LoginFragment extends Fragment implements RequestForgotPassword {
...
//On Forgot Password click
new AsyncForgotPassword(LoginFragment.this, mEmail).execute();
...
@Override
public void onForgotPasswordResult(Result result)
{
if(isAdded())
{
if (result == Result.SUCCESS)
Toast.makeText(getActivity(), "An email has been sent to your account to assist in recovering your password", Toast.LENGTH_LONG).show();
else
Toast.makeText(getActivity(), "We don't have any record of that registered email, sorry! Please try again", Toast.LENGTH_LONG).show();
}
}
...
}
OnTaskCompleted.java
public interface OnTaskCompleted {
enum Result{SUCCESS, FAILURE};
}
RequestForgotPassword.java
public interface RequestForgotPassword extends OnTaskCompleted {
void onForgotPasswordResult(Result result);
}
AsyncForgotPassword.java
public class AsyncForgotPassword extends AsyncTask<Void, Void, JSONObject> {
private RequestForgotPassword mRequest;
private String mEmail;
public AsyncForgotPassword(RequestForgotPassword request, String email)
{
mRequest = request;
mEmail = email;
}
@Override
protected JSONObject doInBackground(Void... params) {
ServerFunctions serverFunctions = new ServerFunctions();
return serverFunctions.forgotPassword(mEmail);
}
@Override
protected void onPostExecute(JSONObject obj) {
try {
JSONObject json1 = obj.getJSONObject("response");
if (json1.getString("success").matches("1")) {
mRequest.onForgotPasswordResult(OnTaskCompleted.Result.SUCCESS);
} else {
mRequest.onForgotPasswordResult(OnTaskCompleted.Result.FAILURE);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
So basically I think it would be good if most async tasks will be written like this. Many will feature progress dialogs and could be long running, or feature meaningful interactions with the calling fragment etc. and I've come across some very interesting discussions here, here, here and here which have led me to approach this set-up.
My understanding of Dianne's code in that first link's answer (written way back in 2010) was that it ensures the activity will never be null when accessed from an async task. My app has only a couple of activities and many fragments so I can't really see how this approach would work as nicely today. I have had problems swapping fragments with async tasks etc. in the past and want to make sure that I won't get any more null Context related problems.
So if I check isAdded()
within a fragment after an async task has complete, is getActivity()
guaranteed to be non-null? Should I call executePendingTransactions() just beforehand everytime just to make sure? Is this all overkill?
Thankyou for any feedback!
Upvotes: 0
Views: 532
Reputation: 2270
from the doc:
public final boolean isAdded ()
Return true if the fragment is currently added to its activity.
So the activity could not be null.
edit:
To achieve a long running task I recommend you to use an IntentService (if the task do complex local operations, otherwise an AsyncTask is ok), save data in DB (or in other forms, for example in SharedPreferences if it is a small chunk of informations) and propagate the logic using a LocalBroadcast. You could also manage a check in onCreate() callback of the fragment so if it was detached when the task was expired, it could retrieve the data persisted and manage them properly; after that, you could flush the data stored previously.
Upvotes: 1