WeakReference/AsyncTask pattern in android - java

I have a question regarding this simple frequently occurring situation in android .
We have a main activity , we invoke an AsyncTask alongwith the reference of the mainactivity , so that that the AsyncTask can update the views on the MainActivity.
I will break down the event into steps
MainActivity creates an AyncTask , passes its reference to it .
AysncTask , starts it's work , downloading ten files for example
The user changed the orientation of the device. This results in an orphan pointer in the AsyncTask
When the AsyncTask completes , and tries to access the activity to update the status , it crashes , because of the null pointer .
The solution for the above is to keep a WeakReference in the AsyncTask as recommended by the book "Pro Android 4"
WeakReference<Activity> weakActivity;
in method onPostExecute
Activity activity = weakActivity.get();
if (activity != null) {
// do your stuff with activity here
}
How does this resolve the situation ?
My question it , if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
What would happen to the previous AsyncTask that was initially invoked ?
Thank you , and I apologize for the length of the question .

How does this resolve the situation ?
The WeakReference allows the Activity to be garbage collected, so you don't have a memory leak.
A null reference means that the AsyncTask cannot blindly try to update a user-interface that is no longer attached, which would throw exceptions (e.g. view not attached to window manager). Of course you have to check for null to avoid NPE.
if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
Depends on your implementation, but probably yes - if you don't deliberately do something to make a repeat download unnecessary, such as caching the results somewhere.
What would happen to the previous AsyncTask that was initially invoked ?
In earlier versions of Android it would run to completion, downloading all of the files only to throw them away (or perhaps cache them, depending on your implementation).
In newer Android's I am suspicious that AsyncTask's are being killed along with the Activity that started them, but my basis for suspicion is only that the memory-leak demo's for RoboSpice (see below) do not actually leak on my JellyBean devices.
If I may offer some advice: AsyncTask is not suitable for performing potentially long running tasks such as networking.
IntentService is a better (and still relatively simple) approach, if a single worker thread is acceptable to you. Use a (local) Service if you want control over the thread-pool - and be careful not to do work on the main thread!
RoboSpice seems good if you are looking for a way to reliably perform networking in the background (disclaimer: I have not tried it; I am not affiliated). There is a RoboSpice Motivations demo app in the play store which explains why you should use it by demo-ing all the things that can go wrong with AsyncTask - including the WeakReference workaround.
See also this thread: Is AsyncTask really conceptually flawed or am I just missing something?
Update:
I created a github project with an example of downloading using IntentService for another SO question (How to fix android.os.NetworkOnMainThreadException?), but it is also relevant here, I think. It has the added advantage that, by returning the result via onActivityResult, a download that is in flight when you rotate the device will deliver to the restarted Activity.

The WeakReference class basically just prevents the JRE to increase the reference counter for the given instance.
I won't go into Java's memory management and answer your question directly: The WeakReference resolves the situation by providing the AsyncTask a way to learn if its parent activity is still valid.
The orientation change itself will not automatically restart the AsyncTask. You have to code the desired behavior with the known mechanisms (onCreate/onDestroy, onSave/RestoreInstanceState).
Concerning the original AsyncTask, I'm not 100 % sure which of these options will happen:
Either Java stops the thread and disposes the AsyncTask, because the only object holding a reference to it (the original Activity) is destroyed
Or some internal Java object maintains a reference to the AsyncTask object, blocking its garbage collection, effectively leaving the AsyncTask to finish in the background
Either way, it would be good practice to abort/pause and restart/resume the AsyncTask manually (or handing it over to the new Activity), or use a Service instead.

How does this resolve the situation ?
It doesn't.
The referent of a WeakReference is set to null when the garbage collector determines that the referent is weakly reachable. This does not happen when an activity is paused, and does not necessarily happen immediately when the activity is destroyed and the framework discards all references to it. If the GC has not run, it is entirely possible for the AsyncTask to complete while its WeakReference still contains a reference to a dead activity.
Not only that, but this approach does nothing to prevent the AsyncTask from uselessly consuming CPU.
A better approach is to have the Activity maintain a strong reference to the AsyncTask and cancel(...) it in the appropriate teardown lifecycle method. The AsyncTask should monitor isCancelled() and stop working if it is no longer needed.
If you want an AsyncTask to survive across configuration changes (but not other forms of activity destruction) you can host it in a retained fragment.

Related

Android show dialog causes illegal state exception if activity recreated

There are three activities: Login, Main and Profile
Obviously from Login you can go to Main. There is a Button that calls AppCompatDialogFragment in Main.
BalanceCalculationDialog dialog = BalanceCalculationDialog.newInstance(model);
dialog.setListener(this);
dialog.show(getSupportFragmentManager(), "TAG");
If I user enters Main for the first time dialog shows.
If user goes to Profile and then backs dialog shows.
If user goes out from Main (finish() with Intent#FLAG_ACTIVITY_NEW_TASK and Intent#FLAG_ACTIVITY_CLEAR_TASK flags) and tries to see dialog he sees an error.
java.lang.IllegalStateException: Can not perform this action after
onSaveInstanceState....CheckStateLoss()...
As far as I know it caused because "Transaction cannot be commited after onSaveInstanceState()".
I tried to override this method leaving empty body, not calling super and didn't work.
Then I found another way to solve problem, which worked:
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
Log.e("ILLEGAL", "Exception", e);
}
}
Nevertheless I don't understand why it works this way -> Number of catching IllegalStateException equals the number of recreating Main. For example I log out 20 times - then I will have 20 catching of mentioned exception. So I don't find this solution as the right one.
In addition, I have other activities that can call another dialogs. These activities also can be finished with finish() and when user opens one of them again he doesn't have the issue.
I would be very glad to have proper explanation of such behaviour and right way of solving it. Thank you very much.
EDIT
Reason of this issue was usage of RxJava EventBus. When I switched to Otto Eventbus problem disappeared. RxJava allows you to send and get messages without subscribe and unsubscribe that can cause unexpected behaviour.
The exception was thrown because you attempted to commit a FragmentTransaction after the activity’s state had been saved, resulting in a phenomenon known as Activity state loss. Before we get into the details of what this actually means, however, let’s first take a look at what happens under-the-hood when onSaveInstanceState() is called. Android applications have very little control over their destiny within the Android runtime environment. The Android system has the power to terminate processes at any time to free up memory, and background activities may be killed with little to no warning as a result. To ensure that this sometimes erratic behavior remains hidden from the user, the framework gives each Activity a chance to save its state by calling its onSaveInstanceState() method before making the Activity vulnerable to destruction. When the saved state is later restored, the user will be given the perception that they are seamlessly switching between foreground and background activities, regardless of whether or not the Activity had been killed by the system.
When the framework calls onSaveInstanceState(), it passes the method a Bundle object for the Activity to use to save its state, and the Activity records in it the state of its dialogs, fragments, and views. When the method returns, the system parcels the Bundle object across a Binder interface to the System Server process, where it is safely stored away. When the system later decides to recreate the Activity, it sends this same Bundle object back to the application, for it to use to restore the Activity’s old state.
So why then is the exception thrown? Well, the problem stems from the fact that these Bundle objects represent a snapshot of an Activity at the moment onSaveInstanceState() was called, and nothing more. That means when you call FragmentTransaction#commit() after onSaveInstanceState() is called, the transaction won’t be remembered because it was never recorded as part of the Activity’s state in the first place. From the user’s point of view, the transaction will appear to be lost, resulting in accidental UI state loss. In order to protect the user experience, Android avoids state loss at all costs, and simply throws an IllegalStateException whenever it occurs.
Solution for this exception is
Be careful when committing transactions inside Activity lifecycle methods. A large majority of applications will only ever commit transactions the very first time onCreate() is called and/or in response to user input, and will never face any problems as a result. However, as your transactions begin to venture out into the other Activity lifecycle methods, such as onActivityResult(), onStart(), and onResume(), things can get a little tricky. For example, you should not commit transactions inside the FragmentActivity#onResume() method, as there are some cases in which the method can be called before the activity’s state has been restored (see the documentation for more information). If your application requires committing a transaction in an Activity lifecycle method other than onCreate(), do it in either FragmentActivity#onResumeFragments() or Activity#onPostResume(). These two methods are guaranteed to be called after the Activity has been restored to its original state, and therefore avoid the possibility of state loss all together. (As an example of how this can be done, check out my answer to this StackOverflow question for some ideas on how to commit FragmentTransactions in response to calls made to the Activity#onActivityResult() method).
Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:
An activity executes an AsyncTask.
The user presses the “Home” key, causing the activity’s
onSaveInstanceState() and onStop() methods to be called.
The AsyncTask completes and onPostExecute() is called, unaware that the
Activity has since been stopped. A FragmentTransaction is committed
inside the onPostExecute() method, causing an exception to be thrown.
In general, the best way to avoid the exception in these cases is to
simply avoid committing transactions in asynchronous callback methods
all together.
Use commitAllowingStateLoss() only as a last resort. The only difference between calling commit() and commitAllowingStateLoss() is that the latter will not throw an exception if state loss occurs. Usually you don’t want to use this method because it implies that there is a possibility that state loss could happen. The better solution, of course, is to write your application so that commit() is guaranteed to be called before the activity’s state has been saved, as this will result in a better user experience. Unless the possibility of state loss can’t be avoided, commitAllowingStateLoss() should not be used.
But I have to agree that this is an Android framework design defect. Google devs try to fix it over many version. But seems like there is no good approach to work-around this. Just understand the life cycle and remember to check the state of activity carefully before committing transaction

What are the Advantages and disadvantages of AsyncTask in Android framework?

I am learning Android app development from Udacity.com by Google engineers and they said,
"It is not a good idea to use 'AsyncTask' as it is not attached to an activity life cycle. The virtual machine will hold on to the activity object as long as the Asynctask is running, even after Android has called onDestroy( ) method for the activity and expect it to be discarded.
If you rotate your phone, the behavior is to destroy the current activity and instantiate a new one. The naive AsyncTask implementation now has two threads trying to do the same update. So it is not the best pattern for a potentially very long running background operation , such as fetching from web services. If you leave the app, the asyncTask will run as long as as the process is kept alive , but will run at a lower priority, and your process will be the first thing to be killed if the device needs more resources. "
1) If using AsyncTask is disadvantageous why was it created? What would have been the design philosophy or the cause to create it in spite of having services(or something similar to achieve same kind of functionality)?
2) What are the situations where Asynctask should be used for betterment compared to Services/similar options available in Android?
3) What are the situations/places Asynctask should never be used?
Please do not downvote this question. I searched Stackoverflow and I couldn't find a similar question.
Advantages of AsyncTask
Provides generic solution for all network calls
Publish progress to UI while executing.
Run Asynchronously
Easy to maintain and read.
Problems in AysncTask
When you rotate your screen, Activity gets destroyed, so AsyncTask will not have a valid reference to publish data from onPostExecute(). In order to retain it, you need to usesetRetainState(true) if calling from fragment or onConfigChanges() if calling from activity method of an activity.
If activity gets finished, AsyncTask execution will not cancelled automatically, you need to cancel them else they will keep on running in the background.
If any exception occurs while performing network task, you need to handle them manually.
Whereas AsycTask, Services, IntentService, Threads all run on different threads and all serve different purpose.
please read more detail here.
So you need to decide when to use which component while performing non UI operations.

runOnUiThread and callbacks strange behavior

At runtime, I am using a callback pattern to have generic code run and use the callback when finished to execute code on the GUI. So in my handler I use the runOnUiThread function against the callback.
These are all async http calls. At runtime if I interrupt and use the back button and go to another fragment for example, the system will swap out the fragments and run both callbacks (the new fragment one on the correct callback, and the old callback that should have ran on the old fragment on the new current fragment). This is wrong, the new fragment gets both callbacks but when it was initialized it was assigned to the other fragment...
This makes no sense and you can only observe the behavior by switching fragments at runtime before an async call finishes. I don't see how it's possible, in the code I check if callback is null so it should have been garbage collected and how it runs on the new callback I don't know how it's possible....there's no additional assignment happening to change this. It's almost like it's just looking in memory for the function signature and running it even though when it does it's on the wrong object.
Does anyone have any clue what's going on? I've surpressed it with an ugly piece code, I just don't know how this is possible?
I understand the obvious candidates are if I assigned it again somewhere, but the answer is no. When the fragment is created it creates an object, assigns itself as the callback, and processing begins so when the fragment is destroyed it should be too. But the async task is still executing which is fine, why it does a callback on the new thread on the main gui I guess is because of runOnUiThread, so that function is somehow changing what the callback object points to in memory?
Its your code :-)
There is no magic happening 'behind the scenes' in Android and no references are changed.
Many have problems like yours, and its always because they assume that the Android system is somehow cleaning up when a fragment or activity instance is replaced or removed. It isn't, and therefore the callbacks are still executed, on a obsolete fragment or activity.
runOnUiThread takes a Runnable and runs it when the UI thread have spare time (after invoking lifecycle methods and finished rendering). Its not magic either :-)

AsyncTask optimization regarding internal list

People , I have an async task which downloads some news items using a webservice . Here is the psuedo for that .
I have an operation that i perform in an asynctask . It downloads some news from a webservice .
AsyncTask
{
AsyncTask(Context context){}
doInBackground()
{
// download some news
..
..
return List<News>
}
onpostExecute(List <News>)
{
is the activity around ? if yes then
//find the list using context ,and populate it with news
}
}
My question is that , in midst of operation if the device is oriented , we will have a new AsyncTask . Now the old one already in progress has an internal List .
Would that be garbage collected ? . I think it wont . I smell the concept of weakreferences being applied on this situation , but I can't trust my nose here .
Also by weakreferences , I may know inside onPostExecute that the activity that spawned this Async is no longer around . But . I still believe I am missing a lot .
Or is this nothing I should worry about and the List would automatically be garbage collected once the asynctask finishes ?
You have a new AsyncTask because the activity is recreated, and you are probably spawning the task in onCreate | onStart | onResume.
You could disable activity restart on config changes, but this is tricky. Just assume your activity might be restarted, and implement it so that this can be done safely.
Since you are passing the context as parameter, I'd assume the task is an static inner class or an external class in its own file.
You could try to use the "application" context instead of the "activity" context, as advised in this blog post:
http://android-developers.blogspot.com.es/2009/01/avoiding-memory-leaks.html
The other option is, as you said, have a WeakReference to the context.
But other than this, the important thing here is whether those two tasks can safely run simultaneously. Also running a task to its completion when you know the result isn't going to be consumed by the visible activity is a waste of resources. I'd cancel the current task when the activity is shutting down (onStop | onPause | onDestroy), but this is also tricky (you need to implement it with the intent of cancelling it ASAP the thread is interrupted. More info here: Android - Cancel AsyncTask Forcefully).
The most robust way to go is to use a Service instead of the AsyncTask.
There is the thread
Is AsyncTask really conceptually flawed or am I just missing something?
Briefly, an Activity is a Controller from the MVC (Model-View-Controller) viewpoint, and your data belong to Model. So just do not invoke such AsyncTasks directly from the Activity.
One more writing: AsyncTask Missteps
http://www.shanekirk.com/2012/04/asynctask-missteps/

File API is causing ANR's

A small part of my application checks if files exist on the user's device. The list of files is potentially quite long - apparently long enough to cause ANR's with a few users. A thousand files is by no means impossible.
The code is quite simple:
new File(fileUrl).exists()
I'm currently doing it on the main thread, as I need the operations to be blocking. I could do it using an AsyncTask class and then continue the rest of the work once it's done, but I'm wondering if that's a valid cause?
All the work is being done in a background Service, if that changes anything. I'm also potentially going to experience orientation changes, and that might be annoying with AsyncTask. Would a Handler be better?
So, to sum things up: Should I do use an AsyncTask for a potentially long-running operation in a background Service, where orientation changes may occur?
Firstly, a Service isn't affected by orientation change - it's only the currently running Activity class which is destroyed / recreated.
Secondly, an AsyncTask isn't of much advantage in a Service as it's designed to be able to interact with the UI. It would give the advantage of doing work on a separate thread but the rest of the methods would basically be redundant.
I'd recommend using an IntentService which manages its own worker thread to do work. See the IntentService documentation

Categories

Resources