Correctly handling fragment interactions from an Async-Task - android

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!

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.

Related

What process is the best way to send on API and save SQLiteDatabase?

I'm new on Android and working an big app which has sending data to API and saving it on SQlite. All of this process is on one class file . But it leaves me on an error. Sometimes the device hanged. other scenario is the data is incomplete . I have read about Intent Service and Services and I want to learn about the two, but I'm wondering how to get all of my data from UI and put it on services. May I know How?
It depends on the nature of the application. If this should happen in response to a user input...you could well use an AsyncTask. Otherwise, a background service could also do the job.
What you should NEVER do is run a network operation and/or database access on the main UI thread.
Services can receive data via intents. The way to send these intents depend on the type of service (Started, Bound or both). There are plenty of resources out there you can read...here's one from Android documentation...
https://developer.android.com/guide/components/services
An Example of an AsyncTask
The example below shows an implementation of AsyncTask that fetches a user's details from a network resource...
public class FetchUserTask extends AsyncTask<String,Void, UserDTO> {
private FetchUserTaskListener listener;
#Override
protected UserDTO doInBackground(String...params){
if(params == null || params.length == 0)
return null;
String userID = params[0];
UserDataProvider provider = new UserDataProvider(userID);
try {
return provider.get(userID);
}
catch(Exception ex){
//log the error
return null;
}
}
#Override
protected void onPostExecute(UserDTO user){
if(listener != null)
listener.onCompleted(user);
}
public void setListener(FetchUserTaskListener listener){
this.listener = listener;
}
public interface FetchUserTaskListener{
void onCompleted(boolean success);
}
}
How'd you use this AsyncTask?
For example, in an Activity, you would use it as below...
public class UserDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//instantiate activity...
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever_layout);
fetchUser(userId);
}
private void fetchUser(String userID){
FetchUserTask task = new FetchUserTask();
task.setListener(new FetchUserTaskListener<UserDTO>() {
#Override
public void onCompleted(UserDTO user) {
//CAUTION: make sure the activity hasn't been stopped before
//accessing any UI elements and/or context
}
}
task.execute(userID);
}
}
Note
You can (and will need to) make the example(s) above a bit more sophisticated. For example you can have the FetchUserTaskListener's onCompleted method return also an error message if an error occurred.
You will also need to check whether the activity has been paused or stopped before you access any context-bound data otherwise you might get an ILlegalStateException.
Make use of SQLiteOpenHelper class and it has methods to be overridden in your own class by extending SQLiteOpenHelper. Create Add, Update, Delete, Get methods as per your requirement in this class and keep this class as Singleton pattern. User Asynctasks to call those methids and you are done.
Hope that helps you visualise things in better way.

Best practice to implement Retrofit callback to recreated activity?

I'm switching to Retrofit and trying to understand proper architecture for using it with async callbacks.
For example I have an interface:
interface RESTService{
#GET("/api/getusername")
void getUserName(#Query("user_id") String userId,
Callback<Response> callback);
}
And I run this from main activity:
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("WEBSITE_URL")
.build();
RESTService api = restAdapter.create(RESTService.class);
api.getUserName(userId, new Callback<Response> {...});
Then user rotates the device and I have newly created activity... What was happen here? How can I get response to the new activity (I assume that api call in background will execute longer than first activity life). Maybe I must use static instance of callback or what? Please show me the right way...
Use otto.
There are a lot of samples to mix otto and retrofit, for example https://github.com/pat-dalberg/ImageNom/blob/master/src/com/dalberg/android/imagenom/async/FlickrClient.java
Or read this post http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
It answers on almost all questions
For potential long running server calls i use an AsyncTaskLoader. For me, the main advantage of Loaders are the activity-lifecycle handling. onLoadFinished is only called if your activity is visible to the user. Loaders are also shared between activity/fragment and orientation changes.
So i created an ApiLoader which uses retrofits synchronous calls in loadInBackground.
abstract public class ApiLoader<Type> extends AsyncTaskLoader<ApiResponse<Type>> {
protected ApiService service;
protected ApiResponse<Type> response;
public ApiLoader(Context context) {
super(context);
Vibes app = (Vibes) context.getApplicationContext();
service = app.getApiService();
}
#Override
public ApiResponse<Type> loadInBackground() {
ApiResponse<Type> localResponse = new ApiResponse<Type>();
try {
localResponse.setResult(callServerInBackground(service));
} catch(Exception e) {
localResponse.setError(e);
}
response = localResponse;
return response;
}
#Override
protected void onStartLoading() {
super.onStartLoading();
if(response != null) {
deliverResult(response);
}
if(takeContentChanged() || response == null) {
forceLoad();
}
}
#Override
protected void onReset() {
super.onReset();
response = null;
}
abstract protected Type callServerInBackground(SecondLevelApiService api) throws Exception;
}
In your activity you init this loader like this:
getSupportLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<ApiResponse<DAO>>() {
#Override
public Loader<ApiResponse<DAO>> onCreateLoader(int id, Bundle args) {
spbProgress.setVisibility(View.VISIBLE);
return new ApiLoader<DAO>(getApplicationContext()) {
#Override
protected DAO callServerInBackground(ApiService api) throws Exception {
return api.requestDAO();
}
};
}
#Override
public void onLoadFinished(Loader<ApiResponse<DAO>> loader, ApiResponse<DAO> data) {
if (!data.hasError()) {
DAO dao = data.getResult();
//handle data
} else {
Exception error = data.getError();
//handle error
}
}
#Override
public void onLoaderReset(Loader<ApiResponse<DAO>> loader) {}
});
If you want to request data multiple times use restartLoader instead of initLoader.
I've been using a kind of MVP (ModelViewPresenter) implementation on my Android apps. For the Retrofit request I made the Activity calls it's respective Presenter, which in turn makes the Retrofit Request and as a parameter I send a Callback with a custom Listener attached to it (implemented by the presenter). When the Callback reach onSuccess or onFailure methods I call the Listener's respective methods, which calls the Presenter and then the Activity methods :P
Now in case the screen is turned, when my Activity is re-created it attaches itself to the Presenter. This is made using a custom implementation of Android's Application, where it keeps the presenters' instance, and using a map for recovering the correct presenter according to the Activity's class.
I don't know if it's the best way, perhaps #pareshgoel answer is better, but it has been working for me.
Examples:
public abstract interface RequestListener<T> {
void onSuccess(T response);
void onFailure(RetrofitError error);
}
...
public class RequestCallback<T> implements Callback<T> {
protected RequestListener<T> listener;
public RequestCallback(RequestListener<T> listener){
this.listener = listener;
}
#Override
public void failure(RetrofitError arg0){
this.listener.onFailure(arg0);
}
#Override
public void success(T arg0, Response arg1){
this.listener.onSuccess(arg0);
}
}
Implement the listener somewhere on the presenter, and on the overrode methods call a presenter's method that will make the call to the Activity. And call wherever you want on the presenter to init everything :P
Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));
Firstly, your activity leaks here because this line:
api.getUserName(userId, new Callback {...})
creates an anonymous Callback class that holds a strong reference to you MainActivity. When the device is rotated before the Callback is called, then the MainActivity will not be garbage collected. Depending on what you do in the Callback.call(), your app may yield undefined behaviour.
The general idea to handle such scenarios is:
Never create a non-static inner class (or an anonymous class as mentioned in the problem).
Instead create a static class that holds a WeakReference<> to the Activity/Fragment.
The above just prevents Leaks. It still does not help you get the Retrofit call back to your Activity.
Now, to get the results back to your component (Activity in your case) even after configuration change, you may want to use a headless retained fragment attached to your Activity, which makes the call to Retrofit. Read more here about Retained fragment - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
The general idea is that the Fragment automatically attaches itself to the Activity on configuration change.
I highly recommend you watch this video given at Google I/O.
It talks about how to create REST requests by delegating them to a service (which is almost never killed). When the request is completed it is immediately stored into Android's built-in database so the data is immediately available when your Activity is ready.
With this approach, you never have to worry about the lifecycle of the activity and your requests are handled in a much more decoupled way.
The video doesn't specifically talk about retrofit, but you can easily adapt retrofit for this paradigm.
Use Robospice
All components in your app which require data, register with the spice service. The service takes care of sending your request to the server (via retrofit if you want). When the response comes back, all components which registered get notified. If there is one of them not available any more (like an activity which got kicked because of rotation), it's just not notified.
Benefit: One single request which does not get lost, no matter whether you rotate your device, open new dialogs/fragments etc...
Using Retrofit2 to handle orientation change. I was asked this in a job interview and was rejected for not knowing it at the time but here it is now.
public class TestActivity extends AppCompatActivity {
Call<Object> mCall;
#Override
public void onDestroy() {
super.onDestroy();
if (mCall != null) {
if (mCall.isExecuted()) {
//An attempt will be made to cancel in-flight calls, and
// if the call has not yet been executed it never will be.
mCall.cancel();
}
}
}
}

Generic asyncTask with callback (for web services)

I've already developed many Android apps that make web service requests, always with the following approach:
In every activity that need to make a web service request, I define an inner AsyncTask that shows a ProgressDialog in onPreExecute(), makes the web service call in doInBackground, and dismisses the progressDialog and updates the results in the UI from onPostExecute().
My concern is: Is there a better (shorter) way to do it? Does it make sense to repeat all that code in every activity? I've been googling a lot, but I've found nothing.
My question is: Couldn't I define a Callback interface? for example this one:
public interface RequestCallback {
public void onSuccess(Whatever whatever);
public void onError(ErrorCode errorCode, String message);
}
... and then define an external class, for example AsyncRequest, that wraps the AsyncTask definition and the ProgressDialog show() and dismiss() statements. So, all activities would just need to instantiate that class, and pass in the following parameters:
1) The method of the web service to run
2) A Bundle with all the parameters of that method of the web service
3) A RequestCallback instance (that could be an anonymous inline instance, where I could update the UI from onSuccess())
4) The context of the Activity (necessary to show the ProgressDialog(), so I would still need a way to prevent configuration change exceptions and so...),
Do you find this a good design? It could save hundreds of lines of code...
Your approach is what I did on my project. And it saved a lot of code as you said, I don't have any complaint about it. But here is some issues that I want to tell you:
You should create new instance of AsyncTask every time you do a background thread to avoid to pile callback.
For the progress dialog, I use it as Singleton, because you don't show many dialogs at the same time. The dialog will be showed when you call the background job, and will be dismiss in the callback. Here is what I did:
private void showProgressDialog(String strMess){
if(null == progressDialog){
progressDialog = new ProgressDialog(MainActivity.this);
}
if(!progressDialog.isShowing()){
progressDialog.setMessage(strMess);
progressDialog.show();
}
}
private void hideProgressDialog(){
if(null != progressDialog && progressDialog.isShowing()){
progressDialog.dismiss();
}
}
void someMethod(){
showProgressDialog("Loading...");
doBackgroundJob(param, new RequestCallBack() {
public void onRequestCompleted(String message, boolean isSuccess) {
hideProgressDialog();
if(isSuccess){
}else{
//do something on error
}
}
});
}
It is an optional, I defined an interface to notify instead of specific class, for each response I use one class, so in base class, I don't care what the response is. Here is it:
public interface OnRequestCompleted<TResponse> {
void requestCompleted(TResponse response);
}
public abstract class BaseRequest<TResponse> implements IRequest{
protected OnRequestCompleted<TResponse> delegate;
protected Class<TResponse> responseClass;
#Override
public void send() {
new HttpTask().execute();
}
private class HttpTask extends AsyncTask<Void, Void, String> {
//...
#Override
protected void onPostExecute(String result) {
if (null != response && null != delegate) {
delegate.requestCompleted(response);
}
}
}
// the response example
public class GroupResponse {
public static class Clip {
public int clipId;
public String detail;
}
public static class Movie {
public int movieId;
public String detail;
}
}
In the subclass of BaseRequest, I will tell it exactly what the response class is (Movie, Clip...)
Hope this help.
If you use it already and it works for you, then yes it makes sense to make it generic and save the time (and bugs) of reimplementing the same thing dozens of times. If you ever find yourself copy-pasting large sections of code with few to no differences you should turn it into a library function or class of some sort. Otherwise if you find a problem later you'll have to fix it in a dozen places. It doesn't even matter if you think of a better way to do things later- its still easier to change it in one place than a dozen.
The only real issue I'd have with your solution is I wouldn't add the progress bar to it- I'd handle it in the calling code and the onSuccess/onError implementations. That way you could also reuse it for a background call that doesn't need to put up a UI. I try to keep my UI decisions as far away from data grabbing code as possible, MVC patterns are good.

How do I handle AsyncTask incompletion in fragments due to screen rotation

I have an App which opens few fragments (actually 3) through a Fragment container (on Tab) and the first fragment uses AsyncTask to download feeds into the app.
The problem occurs when there is screen rotation - the application crashes.
I temporarily handle this problem by loading the data from PostExecute into private static variable but this problem can still occur when the user first enters the app.
This seems a very common or rampant problem but I've not been able to find outright solution here.
I do understand that this is because of configuration changes due to the screen rotation as the AsyncTask is running on a parallel thread to the UI thread.
I do refrain from using the Java threads/executor/executor service at this stage since there seem a ready-made tool in AsyncTask
I have been unable to interrupt the process in order to restart the activity or the fragment successfully and discard the initial subsequent AsyncTask calls..
In other words, how do I destroy the AsyncTask within a Fragment when there has been continuous configuration changes like Screen Rotation. Your help would be greatly appreciated.
public class MolyListFragment extends Fragment {
public final static String MOLY_ARTICLE_DATA = "No Details";
private static final String TAG = "MOLY";
private static ArrayList<MolyPg> mMolyPgs = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mMolyPgs == null) {
new MolyLoadTask().execute(getResources().getString(R.string.mly_feed));
}
else
displayMlicles (mMolyPgs); //this displays the arrayList out of static mMolyPgs
}
private class MolyLoadTask extends AsyncTask<String, Void, ArrayList<MolyPg>> {
#Override
protected ArrayList<MolyPg> doInBackground(String... urls) {
String myUrl = urls[0];
ArrayList<MolyPg> myMlPgs = null;
try {
myMlPgs = loadXmlFromNetwork(myUrl);
} catch (MalformedURLException e) {
Log.d(TAG, "MalformedURLException", e);
} catch (IOException e) {
getResources().getString(R.string.connection_error);
} catch (XmlPullParserException e) {
getResources().getString(R.string.xml_error);
}
return myMlPgs;
}
#Override
protected void onPostExecute(ArrayList<MolyPg> result) {
super.onPostExecute(result);
Do some acrobatics here
}
........
}
}
There are several solutions you could go with, but they all have one common theme...you shouldn't have long-running operations like that so tightly bound directly to your user interface classes.
If I were to make a suggestion, there are two possibilites that often work well:
Implement your AsyncTask inside of a custom AsyncTaskLoader that you can connect to each time your Fragment is started up again to retrieve the data or wait for it to come back. There are a number of callback methods you need to implement, so you'll want to reference the documentation (link). You may also want to look at the source for CursorLoader to get another example of an AsyncTaskLoader implementation.
Create a "data" Fragment in your application whose sole purpose is to manage your background tasks and provide the information when available. This fragment will have no UI component (i.e. don't override onCreateView()) and you will call setRetainInstance() so the FragmentManager keeps only one around. When you add the fragment with a constant tag value, all other components in your UI can easily find it via the FragmentManager to retrieve the data. For more information about this, see the docs section Adding a fragment without a UI.
If you're using fragments you should be using Loaders instead. Check the docs:
http://developer.android.com/reference/android/app/LoaderManager.html
The fragment will find any previously created loaders and "reconnect", so you can get the results from it.
There are ways to "reconnect" AsyncTasks as well, but you're better off just using Loaders.
You can use setRetainInstance(), however as docs say this can only be used with fragments not in the back stack

Background task, progress dialog, orientation change - is there any 100% working solution?

I download some data from internet in background thread (I use AsyncTask) and display a progress dialog while downloading. Orientation changes, Activity is restarted and then my AsyncTask is completed - I want to dismiss the progess dialog and start a new Activity. But calling dismissDialog sometimes throws an exception (probably because the Activity was destroyed and new Activity hasn't been started yet).
What is the best way to handle this kind of problem (updating UI from background thread that works even if user changes orientation)? Did someone from Google provide some "official solution"?
Step #1: Make your AsyncTask a static nested class, or an entirely separate class, just not an inner (non-static nested) class.
Step #2: Have the AsyncTask hold onto the Activity via a data member, set via the constructor and a setter.
Step #3: When creating the AsyncTask, supply the current Activity to the constructor.
Step #4: In onRetainNonConfigurationInstance(), return the AsyncTask, after detaching it from the original, now-going-away activity.
Step #5: In onCreate(), if getLastNonConfigurationInstance() is not null, cast it to your AsyncTask class and call your setter to associate your new activity with the task.
Step #6: Do not refer to the activity data member from doInBackground().
If you follow the above recipe, it will all work. onProgressUpdate() and onPostExecute() are suspended between the start of onRetainNonConfigurationInstance() and the end of the subsequent onCreate().
Here is a sample project demonstrating the technique.
Another approach is to ditch the AsyncTask and move your work into an IntentService. This is particularly useful if the work to be done may be long and should go on regardless of what the user does in terms of activities (e.g., downloading a large file). You can use an ordered broadcast Intent to either have the activity respond to the work being done (if it is still in the foreground) or raise a Notification to let the user know if the work has been done. Here is a blog post with more on this pattern.
The accepted answer was very helpful, but it doesn't have a progress dialog.
Fortunately for you, reader, I have created an extremely comprehensive and working example of an AsyncTask with a progress dialog!
Rotation works, and the dialog survives.
You can cancel the task and dialog by pressing the back button (if you want this behaviour).
It uses fragments.
The layout of the fragment underneath the activity changes properly when the device rotates.
I've toiled for a week to find a solution to this dilemma without resorting to editing the manifest file. The assumptions for this solution are:
You always need to use a progress dialog
Only one task is performed at a time
You need the task to persist when the phone is rotated and the progress dialog to be automatically dismisses.
Implementation
You will need to copy the two files found at the bottom of this post into your workspace. Just make sure that:
All your Activitys should extend BaseActivity
In onCreate(), super.onCreate() should be called after you initialize any members that need to be accessed by your ASyncTasks. Also, override getContentViewId() to provide the form layout id.
Override onCreateDialog() like usual to create dialogs managed by the activity.
See code below for a sample static inner class to make your AsyncTasks. You can store your result in mResult to access later.
final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
public OpenDatabaseTask(BaseActivity activity) {
super(activity, MY_DIALOG_ID); // change your dialog ID here...
// and your dialog will be managed automatically!
}
#Override
protected Void doInBackground(Void... params) {
// your task code
return null;
}
#Override
public boolean onAfterExecute() {
// your after execute code
}
}
And finally, to launch your new task:
mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
That's it! I hope this robust solution will help someone.
BaseActivity.java (organize imports yourself)
protected abstract int getContentViewId();
public abstract class BaseActivity extends Activity {
protected SuperAsyncTask<?, ?, ?> mCurrentTask;
public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
if (mCurrentTask != null) {
mCurrentTask.attach(this);
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
mCurrentTask.postExecution();
}
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
mDialogMap.put(id, true);
}
#Override
public Object onRetainNonConfigurationInstance() {
if (mCurrentTask != null) {
mCurrentTask.detach();
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
return mCurrentTask;
}
}
return super.onRetainNonConfigurationInstance();
}
public void cleanupTask() {
if (mCurrentTask != null) {
mCurrentTask = null;
System.gc();
}
}
}
SuperAsyncTask.java
public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected BaseActivity mActivity = null;
protected Result mResult;
public int dialogId = -1;
protected abstract void onAfterExecute();
public SuperAsyncTask(BaseActivity activity, int dialogId) {
super();
this.dialogId = dialogId;
attach(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mActivity.showDialog(dialogId); // go polymorphism!
}
protected void onPostExecute(Result result) {
super.onPostExecute(result);
mResult = result;
if (mActivity != null &&
mActivity.mDialogMap.get((Integer) dialogId) != null
&& mActivity.mDialogMap.get((Integer) dialogId)) {
postExecution();
}
};
public void attach(BaseActivity activity) {
this.mActivity = activity;
}
public void detach() {
this.mActivity = null;
}
public synchronized boolean postExecution() {
Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
if (dialogExists != null || dialogExists) {
onAfterExecute();
cleanUp();
}
public boolean cleanUp() {
mActivity.removeDialog(dialogId);
mActivity.mDialogMap.remove((Integer) dialogId);
mActivity.cleanupTask();
detach();
return true;
}
}
Did someone from Google provide some "official solution"?
Yes.
The solution is more of an application architecture proposal rather that just some code.
They proposed 3 design patterns that allows an application to work in-sync with a server, regardless of the application state (it will work even if the user finishes the app, the user changes screen, the app gets terminated, every other possible state where a background data operation could be interrumpted, this covers it)
The proposal is explained in the Android REST client applications speech during Google I/O 2010 by Virgil Dobjanschi. It is 1 hour long, but it is extremely worth watching.
The basis of it is abstracting network operations to a Service that works independently to any Activity in the application. If you're working with databases, the use of ContentResolver and Cursor would give you an out-of-the-box Observer pattern that is convenient to update UI without any aditional logic, once you updated your local database with the fetched remote data. Any other after-operation code would be run via a callback passed to the Service (I use a ResultReceiver subclass for this).
Anyway, my explanation is actually pretty vague, you should definititely watch the speech.
While Mark's (CommonsWare) answer does indeed work for orientation changes, it fails if the Activity is destroyed directly (like in the case of a phone call).
You can handle the orientation changes AND the rare destroyed Activity events by using an Application object to reference your ASyncTask.
There's an excellent explanation of the problem and the solution here:
Credit goes completely to Ryan for figuring this one out.
After 4 years Google solved the problem just calling setRetainInstance(true) in Activity onCreate. It will preserve your activity instance during device rotation. I have also a simple solution for older Android.
you should call all activity actions using activity handler. So if you are in some thread you should create a Runnable and posted using Activitie's Handler. Otherwise your app will crash sometimes with fatal exception.
This is my solution: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
Basically the steps are:
I use onSaveInstanceState to save the task if it is still
processing.
In onCreate I get the task if it was saved.
In onPause I discard the ProgressDialog if it is shown.
In onResume I show the ProgressDialog if the task is still
processing.

Categories

Resources