I have used AsyncTasks with my application, in order to lazy download and update the UI.
For now my AsyncTasks updates the UI real simply:
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
gender.setText(values[0]);
}
My problem is how to check if the activity which the gender TextView rendered from, is still available?
If not, I will get an error and my application will shut down.
You can cancel your asynctask in the activity's onDestroy
#Override
protected void onDestroy() {
asynctask.cancel(true);
super.onDestroy();
}
and when performing changes you check whether your asynctask has been cancelled(activity destroyed) or not
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if(!isCancelled()) {
gender.setText(values[0]);
}
}
I had a similar problem - essentially I was getting a NPE in an async task after the user had destroyed the fragment. After researching the problem on Stack Overflow, I adopted the following solution:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Then, I check "if running" periodically in my async code. I have stress tested this and I am now unable to "break" my activity. This works perfectly and has the advantage of being simpler than some of the solutions I have seen on SO.
Try
if (!isFinishing()) {
gender.setText(values[0]);
}
Check whether activity is running or not
if (!isFinishing()) {
// Do whatever you want to do
}
I will insist that you that if you Activity is not running why don't you cancel the AsyncTask?
That would be a better and feasible solution. If you Application is running say you move from one Activity to another then it won't give error AFAIK.
But, I would insist to cancel the AsyncTask then you'r Activity is not running, you can check AsyncTask is running or not,
if(task != null && task.equals(AsyncTask.Status.RUNNING))
task.cancel(true);
Even though, I have never faced this scenario; I will try to answer your question.
In your case you will need to validate the Context passed to AsyncTask.
You can perform validation
if(null!=mContext) //Activity still exist!!
{
gender.setText(values[0]);
}
else //Activity is destroyed
{
//Take appropriate action!!
}
The advantage will be, if the activity is destroyed by the time you reach this statement, your Context will automatically become null and you can handle the scenario.
As this part of one training on Android Developers suggests, keep a WeakReference on the UI element that needs to be updated after task is done and check if the reference is null before using it. This helps not only in checking if the UI is still around, but also does not prevent UI elements from being garbage collected.
Shouldn't
if (gender) {
gender.setText(values[0]);
}
be enough?
Related
How does using RxJava (or RxAndroid,etc) instead of AsyncTask in Android help prevent a context leak? In AsyncTask, if you execute it and user leaves the app, then the activity context can be null and the app can crash. I have heard that RxJava can help prevent this type of crash when doing threading. I also heard that it can do better error handling then the doInBackground method of AsyncTask (which handles errors badly). Most of the time I just return null (for example) in doInBackground if anything fails, but I've read that RxJava can return the exact error and not leak. Can anyone give an example?
Here is a small demo of a crash in AsyncTask if the user leaves the app while it's trying to report results to UI:
#SuppressWarnings("unused")
private class GetTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(String result) {
pd = new ProgressDialog(getActivity().getApplicationContext());//can crash right here
pd.setTitle("Grabbing Track!");
pd.setMessage("Please wait...");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}}
And here is a doInBackground method call that does not send out errors that are useful:
#Override
protected String doInBackground(String... params) {
String myIntAsString = 1/0 + ""; //this should give an error (how do we report it to the caller??
//or if we are parsing json and it fails, how do we report it to the caller cleanly. Can RxJava help?
}
I think the good thing about RxJava is if you have a bunch of tasks you can put them in a sequence such that you know when one finishes and the next is about to start. in a AsyncTask if you have more then one running, you have NO guarantee which task will complete first and then you have to do alot of error checking if you care about order. So RxJava allows you to sequence calls.
In regards to memory leaks we can have a AsyncTask as an inner class of an activity. Now since its tied to the activity when the activity is destroyed that context is still hanging around and wont be garbage collected, thats the memory leak part.
Here is where RxJava can help. if any errors occur at all then we can call the subscribers onError method. The subcriber can look like this:
public Observable<JsonObject> get_A_NetworkCall() {
// Do your network call...but return an observable when done
}
Subscription subscription = get_A_NetworkCall()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<jsonResponse>() {
#Override
public void onCompleted() {
// Update UI
}
#Override
public void onError() {
// show error on UI
}
#Override
public void onNext(JsonObject response) {
// Handle result of jsonResponse
}
});
or something similar - this is psuedocode . The point is you can report the errors more cleanly and switch threads in one line. Here we are reporting on androids main thread but doing the work on a new thread. After we are done in our activities onDestroy method we can simply unsubscribe to the observable and it kills it and prevents any memory leaks that we encounter with AsyncTask. This to me should be the replacement for any asyncTasks.
I use a combination of two things. First, RxAndroid is really helpful:
https://github.com/ReactiveX/RxAndroid
You can use AppObservable.bindActivity on it to bind an observable such that its output is observed on the main thread, and if the activity is scheduled to be destroyed, messages will not be forwarded. You still have to manage the pause/resume lifecycle though. For that, I use a composite subscription like this (pseudojava coming up):
public class MyActivity extends Activity {
private final CompositeSubscription subscriptions = new CompositeSubscription();
#Override
public void onResume() {
super.onResume();
subscriptions.add(AppObservable.bindActivity(this, myObservable)
.subscribe());
subscriptions.add(AppObservable.bindActivity(this, myOtherObservable)
.subscribe());
}
#Override
public void onPause() {
subscriptions.clear();
super.onPause();
}
}
Obviously you'd want to do more in subscribe if you want to do something with the data, but the important thing is to collect the returned Subscription instances and add them to the CompositeSubscription. When it clears them out, it will unsubscribe them as well.
Using these 'two weird tricks' should keep things from coming back to the Activity when it is an inoperative state.
I am seeing some strange behaviour with onPause / onResume in my app and cannot work out what is happening.
I perform a database query (simple subclass of AsyncTask) in onResume and cancel it in onPause if it is still executing. I received a crash report that made me wonder if the task cancel was working or not so added an analytics event to record onPostExecute getting called after onPause had cancelled the task.
Over the last month I have seen 140 of these events for 4,100 page views.
#Override
protected void onResume() {
super.onResume();
mIsResumed = true;
if (mReverseCardsTask == null) {
mReverseCardsTask = new TcgCursorTask(this) {
#Override
protected Cursor doInBackground(Void... params) {
return mDb.reverseFetchCards();
}
#Override
protected void onPostExecute(Cursor cursor) {
if (mIsResumed) {
onReverseCardsCursor(cursor);
} else {
EasyTracker.getTracker().sendEvent("error", "on-post-execute", "called after paused", 1L);
}
}
};
mReverseCardsTask.execute();
}
}
#Override
protected void onPause() {
super.onPause();
mIsResumed = false;
if (mReverseCardsTask != null) {
mReverseCardsTask.cancel(false);
mReverseCardsTask = null;
}
}
I have a feeling I am missing something very simple here, but can't see it.
I just noticed I am not clearing mReverseCardsTask in onPostExecute, but that should not matter.
Just calling cancel() doesn't do anything. You actually have to put checks in the process to determine if it is to be canceled and do the job of canceling it.
The OS doesn't know what you may need to do to clean things up (like closing files or open network connections) before stopping.
OK. I have worked it out. I am not sure which API version it was fixed in, but if you look at the code for Gingerbread there is a clear race condition in the cancel() handling. The GUI thread code which processes the MESSAGE_POST_RESULT message from the background calls onPostExecute() regardless of whether or not the task was cancelled.
It turns out that the fix is quite simple. All I need to do is add my own check of isCancelled() before executing my onPostExecute() logic.
The Gingerbread code receives MESSAGE_POST_RESULT and calls finish(). Then finish() calls onPostExecute().
I want to stop a AsyncTask thread from another AsyncTask thread. I have tried like
new AsyncTask.cancel(true) to stop the background process but it didn't stop.
Could any one help me on this?
declare your asyncTask in your activity:
private YourAsyncTask mTask;
instantiate it like this:
mTask = new YourAsyncTask().execute();
kill/cancel it like this:
mTask.cancel(true);
The reason why things aren't stopping for you is because the process (doInBackground()) runs until it is finished. Therefore you should check if the thread is cancelled or not before doing stuff:
if(!isCancelled()){
// Do your stuff
}
So basically, if the thread is not cancelled, do it, otherwise skip it :)
Could be useful to check for this some times during your operation, especially before time taking stuff.
Also it could be useful to "clean up" alittle in
onCancelled();
Documentation for AsyncTask:
http://developer.android.com/reference/android/os/AsyncTask.html
Hope this helps!
You may also have to use it in onPause or onDestroy of Activity Life Cycle:
//you may call the cancel() method but if it is not handled in doInBackground() method
if (loginTask != null && loginTask.getStatus() != AsyncTask.Status.FINISHED)
loginTask.cancel(true);
where loginTask is object of your AsyncTask
Thank you.
You can't just kill asynctask immediately. In order it to stop you should first cancel it:
task.cancel(true);
and than in asynctask's doInBackground() method check if it's already cancelled:
isCancelled()
and if it is, stop executing it manually.
I had a similar problem - essentially I was getting a NPE in an async task after the user had destroyed the fragment. After researching the problem on Stack Overflow, I adopted the following solution:
volatile boolean running;
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
running=true;
...
}
public void onDestroy() {
super.onDestroy();
running=false;
...
}
Then, I check "if running" periodically in my async code. I have stress tested this and I am now unable to "break" my activity. This works perfectly and has the advantage of being simpler than some of the solutions I have seen on SO.
u can check onCancelled() once then :
protected Object doInBackground(Object... x) {
while (/* condition */) {
if (isCancelled()) break;
}
return null;
}
I need my Android app to periodically fetch data from a server using AJAX calls, and update the UI accordingly (just a bunch of TextViews that need to be updated with setText()). Note that this involves 2 tasks:
Making an AJAX call, and updating the UI once I receive a response - I use a simple AsyncTask for this.
Doing the above repeatedly, at regular intervals.
I haven't figured out an elegant way to achieve Point 2 above. Currently, I am simply executing the task itself from OnPostExecute(). I read on this thread at SO that I need not worry about garbage collection as far as the AsyncTask objects are concerned.
But I'm still unsure as to how I set up a timer that will fire my AsyncTask after it expires. Any pointers will be appreciated. Here is my code:
public class MyActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new AjaxRequestTask().execute(MY_REST_API_URL);
}
private void updateReadings(String newReadings) {
//Update the UI
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX Request
}
#Override
protected void onPostExecute(String result) {
updateReadings(result);
/*Is there a more elegant way to achieve this than create a new AsyncTask object every 10 seconds? Also, How can I update the UI if I create a timer here? */
new AjaxRequestTask().execute(MY_REST_API_URL);
}
}
}
Thanks in advance
EDIT:
I tried posting an answer but couldn't do it since I don't have the reputation to answer within 8 hours.
Well, so I found a solution. I'm not convinced however.
protected void onPostExecute(String result) {
updateReadings(result);
// super.onPostExecute(result);
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
new AjaxRequestTask().execute(MY_REST_API_URL);
}
},
TIMER_ONE_TIME_EXECUTION_DELAY
);
}
Are there any flip sides that I should be aware of when I use this? In particular, I am seeing lots of GCs happening in the LogCat. Also, I am wondering how an AsyncTask can be candidate for GC unless the onPostExecute() completes?
How can I "stop" the updates? One way I thought of was to make the very first AsyncTask instance as a member variable of the Activity. That way, I can invoke cancel(true) on it and hope that this will "stop" the tasks.
SOLUTION:
In case anyone is looking for something similar - none of the solutions I mentioned here work satisfactorily. They all suffer from OutOfMemory issues. I did not debug into the details of the OOM, but I suspect it could either be because of the recursion, or because of having HTTP-related objects as member variables in the AsyncTask rather than as members of the Activity (basically because of NOT reusing HTTP and other objects).
I discarded this approach for a different one - making my Ajax Calls endlessly in the doInBackground() of my AsyncTask; and updating the UI in onProgressUpdate(). That way I also avoid the overhead of maintaining too many threads or Handlers for updating the UI (remember UI can be updated in onProgressUpdate() ).
This approach also eliminates the need for Timers and TimerTasks, favoring the use of Thread.sleep() instead. This thread on SO has more details and a code snippet too.
Call postDelayed() on any View to schedule a hunk of code to be run on the main application thread after a certain delay. Do this in onPostExecute() of the AsyncTask to create and execute another AsyncTask.
You could use AlarmManager, as others have cited, but I would agree with you that it feels a bit like overkill for timing that occurs purely within an activity.
That being said, if the AJAX calls should be occurring regardless of whether the activity exists, definitely consider switching to AlarmManager and an IntentService.
I think the android way to do this is using AlarmManager. Or you can user a basic java Timer as well. I'd recommend AlarmManager.
Set it up to send some intent with a custom Action, and register a broadcastreceiver for it.
If the ajax calls are only executed in the activity you can just use a timer in the activity which starts the tasks.
Otherwise use a service which uses the AlarmManager and which connects to the gui via a broadcast.
The recommended way to do a repeated task, is via AlarmManager, as alluded to by Scythe. Basically it involves setting up a broadcast listener, and having AlarmManager fire off an intent to that listener at whatever interval you choose. You then would have your broadcast listener call out to the activity to run the AsyncTask. If you need a very tight timer (less than 5s calls I'd say), then you're better off using a Timer within a Service, and using AIDL to call back to the activity.
Instead of talking directly from the broadcast intent, you could also setup an IntentService which you can poke, and use AIDL to update the activity.
This is how I achieved it finally. Note that the AsyncTask cancel(true) method is useless in my scenario because of the recursion. I used what #CommonsWare suggested - used a flag to indicate whether any more tasks should be executed.
public class MyActivity extends Activity {
/*Flag which indicates whether the execution should be halted or not.*/
private boolean mCancelFlag = false;
private AjaxRequestTask mAjaxTask;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if(mAjaxTask == null){
mAjaxTask = new AjaxRequestTask();
}
mAjaxTask.execute(MY_REST_API_URL);
}
#Override
protected void onResume() {
super.onResume();
mCancelFlag = false; /*when we resume, we want the tasks to restart. Unset cancel flag*/
/* If the main task is Finished, create a new task and execute it.*/
if(mAjaxTask == null || mAjaxTask.getStatus().equals(AsyncTask.Status.FINISHED)){
new AjaxRequestTask().execute(TLS_REST_API_URL);
}
}
#Override
protected void onPause() {
mCancelFlag = true; /*We want the execution to stop on pause. Set the cancel flag to true*/
super.onPause();
}
#Override
protected void onDestroy() {
mCancelFlag = true;/*We want the execution to stop on destroy. Set the cancel flag to true*/
super.onDestroy();
}
private void updateReadings(String result) {
//Update the UI using the new readings.
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
private AjaxRequestTask mChainAjaxRequest;
private Timer mTimer;
private TimerTask mTimerTask;
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX call and get the response
return ajaxResponse;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "Updating readings");
updateReadings(result);
// super.onPostExecute(result);
if(mTimer == null){
mTimer = new Timer();
}
if(!mCancelFlag){/*Check if the task has been cancelled prior to creating a new TimerTask*/
if(mTimerTask == null){
mTimerTask = new TimerTask() {
#Override
public void run() {
if(!mCancelFlag){/*One additional level of checking*/
if(mChainAjaxRequest == null){
mChainAjaxRequest = new AjaxRequestTask();
}
mChainAjaxRequest.execute(MY_REST_API_URL);
}
}
};
}
mTimer.schedule(mTimerTask,TIMER_ONE_TIME_EXECUTION_DELAY);
}
}
}
}
In my Activity I use multiple AsyncTask classes.
How to cancel AsyncTask when Activity finishes?
i think the best place to do this is onStop
protected void onStop() {
super.onStop();
/*
* The device may have been rotated and the activity is going to be destroyed
* you always should be prepared to cancel your AsnycTasks before the Activity
* which created them is going to be destroyed.
* And dont rely on mayInteruptIfRunning
*/
if (this.loaderTask != null) {
this.loaderTask.cancel(false);
}
}
in my Task i then check as often as possible if cancel was called
protected String doInBackground(String... arg0) {
if (this.isCancelled()) {
return null;
}
}
and of course dont forget to drop data that maybe returned since there's no more Activity to receive it
protected void onPostExecute(List<UserStatus> result) {
if(!this.isCancelled()) {
//pass data to receiver
}
}
I don't understand if your "cancel" means rollback but you have a cancel method on the AsyncTask class.
The asynctask thread is kept alive in a thread pool for future istances of AsyncTask. You can't remove them.