Android - Do I have to have WeakReference for inner class AsyncTask? - android

LEt's say I have an inner class AsyncTask in an Activity class.
Do I have to have weakRefeernce in this AsyncTask?
Also, do I have to have weakReference for AsyncTask all the time?
Lastly, if I declare inner class AsyncTask as static, is it safe?

Yes always declare your Asynctask static if it's an inner class or just create it's own class, try avoiding declaring as non-static inner class, as it creates memory leaks. Read here for a more elaborate explanation why you should not do this
You do not want to use WeakReference for an Asynctask since the garbage collector of android 2.3+ is very aggressive and will collect all weak refrences very fast. Also weak reference generally is NOT a good pattern for threads, its more for memory footprint heavy obects like bitmaps (but not anymore in android as said before).
From http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective.

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. Hence it is a good practice to use WeakReference to handle the above mentioned situation.
And yes, it is perfectly safe to declare your inner class as static
WeakReference<Activity> weakActivity;
Somewhere in AsyncTask, probably either constructor or onPreExecute:
weakActivity = new WeakReference<Activity>(activity);
In onPostExecute:
Activity activity = weakActivity.get();
if (activity != null) {
// do your stuff with activity here
}
from the activity in which you are starting the AsyncTask,pass this to the constructor. (I assume you write the assignment statement shown above in your constructor.

If you're using the WeakReference for the right reason, then having an aggressive garbage collector shouldn't bother you.
WeakReference means that you're not interested in keeping that object in memory if no one else is strongly pointing to it.
For example, having an AsyncTask that is passed an ImageView to load an image into.
After the AsyncTask is done with it's work if no one is pointing to the image view with a strong reference, that means that the ImageView is no longer visible to the user and it's view has been destroyed, so why would you possibly want to have a reference to a view that has been taken off screen.
So YES i believe that all of your references in an AsyncTask should be weak specially ones that is related to the UI "Views, Activity" stuff like that.
And YES you should have your inner classes declared as static as much as possible, so they don't have an implicit reference to its containing class, which will destroy what we have tried to do by using WeakReference

Related

Why can't Android release the finished activity from memory after AsyncTask finishes?

So I read about memory leaks in Android regarding AsyncTask, and in essence, as far as I understood it's when activity is destroyed for some reason but AsyncTask isn't done, so because AsyncTask has a strong reference to Activity, GC can't collect Activity, i.e. can't release it from memory. My question is, if/when AsyncTask finishes its work and is no longer needed, why can't GC collect that referenced Activity then? Why would that be a problem?
Generally, inner classes often outlive their parents which is why the parent class become not eligible for GC. As for AsyncTask, if make your async task inner and provide it with a context of your activity, Android studio will warn that it might cause memory leaks.
This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask) A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is, for example, a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these long-running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts.
Non-static inner class outlives its parent. But when you don`t declare it inner that means that you do not have access to parent class members anymore. You can pass in a reference to the Context of your activity but then you run the same risk of a memory leak.
Solution?
A possible solution is to provide a weak reference to your context. Another solution is to cancel your async task in onStop() callback of your activity. Moreover, you can confirm the state of your activity using activityContext.isFinishing(). If the state equals true, simply cancel or return your async task.
A little off topic. Async tasks are a thing of the past, there are other better methods to handle concurrency like RxJava, Kotlin`s coroutines and etc.

MemoryLeak through onConfigurationChanged()

I have an AsyncTask which I need to "restart" if the user does configurations such as Switch Color.
When he do so I start the AsyncTask like this:
myWorkerClass.clearMemory();
myWorkerClass = new WorkerClass(getApplicationContext(), gv, searchbar, width, scaleButtonText);
myWorkerClass.execute();
In the AsyncTask I add a onTextChangeListener to my EditText!(Which causes the MemoryLeak later).
To prevent MemoryLeaks I have a Method in my AsyncTask which removes the onTextChangedListener:
public void clearMemory() {
searchbar.removeTextChangedListener(myTextWatcher);
}
Everything works fine except when I rotate my device. When I rotate my Device I do only this in onConfigurationChanged:
myWorkerClass.clearMemory();
myWorkerClass = new WorkerClass(getApplicationContext(), gv, searchbar, width, scaleButtonText);
myWorkerClass.execute();
As you can see I do exactly the same thing as if the user changes a Color. But at rotating device I'm leaking Memory, at switching Color I'm not!
This is after switching the Color:
This is after rotating the Screen a few times (remember I do exactly the same as at switching color:
These are my Leak Suspects from the Heap Dump:
This is my dominator tree:
Why do I know the onTextChangeListener is the Problem?
Because if I comment adding a onTextChangedListener to my EditText out, everything works fine. No Memory Leaks.
My Question:
Why does a Rotation Change leak Memory and a Color Change does not when I start the asynctask the exact same way and do exact the same things within the asynctask?
I searched a little bit: http://developer.android.com/guide/topics/resources/runtime-changes.html
But I can't figure out if that is my Problem. The Rotation must do something different, like creating a new activity because of that creating a new reference to my edittext. And because of that he can't remove the old onTextChangeListener.
Please understand. I don't want to make my whole code public. But I think this isn't necessary anyway in this case.
The Rotation must do something different, like creating a new activity because of that creating a new reference to my edittext.
exactly, it destroys your current activity and creates a new one. If searchbar is a member variable of your AsyncTask then consider putting it into WeakReference:
WeakReference<SearchBar> searchbarPtr;
then access using searchBarPtr.get(), but check if its null, if so then it means it was garbage collected due to config change.
Also remember to not make your AsyncTask an inner class of you activity. If it is nested then make it static. Otherwise your asynctask will keep reference to your activity and it will prevent it from being destroyed - until its thread ends.
Unfortuanately making it all work corectly in all situations can be quite difficult and time consuming.
Hopefully no one will suggest preventing destruction of your activity through android:configChanges, implementing correct behaviour of your activity during rotation will prevent it from crashing/leaking in less common activity lifecycle moments which cannot be prevented by android:configChanges.
In android, the rotation destroys your current activity to start a new one.
To avoid it, you can add android:configChanges="orientation|screenSize" in your manifest file.
Below are tips to avoid memory leaks on Rotation change
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
A garbage collector is not an insurance against memory leaks
Source: Avoiding memory leaks

Android: Is it good practice to store Views as global variable in class?

I am new to Android, and have been reading that memory management is very important in those memory limited apps.
I have read some where that activity's findViewById() is very expensive. So i'm wondering, if it is good practice to store the Views that you will be using as a global variable and reuse the object? Or is it better to run findViewById() everytime i need to use the view?
Thanks,
Kev
If what you mean is static variable by global variable, then never ever do that! If you keep views as static variables, the activities holding the views will leak.
All the views that you see on the screen are attached to a certain activity, and they hold a reference to the activity, if you keep a static reference to one of the views, the activity will never be garbage collected when the activity is killed(either by pressing the BACK key or you call the finish() method on the activity).
As for findViewById(), I don't think you need to care much about the performance of it, it may expose some overhead, but it is only relatively expensive, it's fast enough for most apps.
When you say "store a view as a global variable", I guess you mean "keeping a reference to the view as a private property in the Activity class". This should not be a problem as far as memory is concerned: keeping an additional reference to the view does not mean storing the whole object again in memory.
What you have to keep in mind is that if you keep a reference to an object, the garbage collector will not be able to clean it from memory even if it is not needed anymore. But since your view is probably not supposed to be destroyed before your activity is, keeping a reference to it in the Activity class should not lead to memory leaks.
So in short: if you need to access your view frequently in your Activity class, I would say it's good practise. At least this is what I do, so if anybody disagrees I'd be very interested to know why.
Yes it is better to make Views as member variables of your activity.(in java there are no global variables, the variables that are declared in a class are called member variables).
public MyActivity extends Activity{
private View mView;
public void onCreate(Bundle savedState){
super.onCreate(savedState);
setContentView(layout);
mView = findViewById(id);
}
}
Storing it as a private member of your class is typical for something you will be using multiple times. A pointer to an object like that. Is tiny.

Is it acceptable to maintain an instance of Context in a class not derived from Activity?

I am developing an application where I've passed the Activity's context into a class derived from Object.
Is there anything wrong with doing this?
Maintaining an instance of a Context like that of an Activity or Service is going to open up ways for memory leaks.
However, keeping a reference to the instance returned by getApplicationContext() should be harmless.
There's nothing inherently wrong at all. Every View class does that, for instance.
The only danger is maintaining a reference to an Activity after it is destroyed. This is a common source of memory leaks. See the blog post Avoiding memory leaks for more information about this.
View subclasses avoid leaking because references to the views themselves generally go away when the activity is destroyed. If your class instance that is maintaining a reference does not go away like that, then you need to arrange for the reference to go away. One option is to override onDestroy for your activity and do some clean-up there. Another is to use a SoftReference instead of a hard reference to the context.

Android context leaks in AsyncTask

If I interpret this article correctly, passing the activity context to AsyncTasks is a potential leak, as the activity might be destroyed while the task is still running.
How do you deal with this in AsyncTasks that are not inner clases and need access to resources or to update the UI?
Additionally, how can you avoid leaking the context if you need references to progress dialogs to dismiss them?
If I understand your question correctly: Java's WeakReference or SoftReference class is a good fit for this type of situation. It will allow you to pass the context to the AsyncTask without preventing the GC from freeing the context if necessary.
The GC is more eager when collecting WeakReferences than it is when collecting SoftReferences.
instead of:
FooTask myFooTask = new FooTask(myContext);
your code would look like:
WeakReference<MyContextClass> myWeakContext = new WeakReference<MyContextClass>(myContext);
FooTask myFooTask = new FooTask(myWeakContext);
and in the AsyncTask instead of:
myContext.someMethod();
your code would look like:
myWeakContext.get().someMethod();

Categories

Resources