Can using context in helper class cause memory leak in android
I have a helper class with the following method
public class HelperClass {
private Context context;
public HelperClass(Context context) {
this.context = context;
}
public void Addfiles(Context context, String Filename) {
try {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(Filename);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
In my MainActivity I want to call it like this
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
HelperClass h = new HelperClass(this);
h.Addfiles(this,Filename);
}
}
I want to know can using context like this cause memory leaks , and if so , how to deal with it.
TL;DR - strictly in the case above, no, since MyActivity won't be destroyed while in "onCreate"
Long answer:
Typically memory leaks happen when an object cannot be garbage collected because something still references it, when the garbage collector runs.
In your case, the activity is just being created, and your primary concern is whether leaks can occur, since HelperClass holds a reference to it.
First of all, in normal conditions (excepting when AOS might kill your app) MainActivity is guaranteed to not get garbage collected before its onDestroy method will be called. This is because until then (and possibly for some time after that - not really relevant) it will be referenced by the Android Framework itself.
HelperClass on the other hand, is a local variable inside onCreate. After onCreate will finish, HelperClass might be garbage collected at any point, since it won't have any object referencing it (see here). So the chances of HelperClass being GC'd before onDestroy is called are quite high. If this happens then there will be no reference to MainActivity --> no leaks.
Event if HelperClass won't be garbage collected until onDestroy is called on MainActivity, you still won't have leaks since the garbage collector is smart enough to be able to clean up cyclic references.
What needs to happen for leaks to occur is that an object which outlives your Activity will hold a reference to it, or its context.
Assume your code would be like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final HelperClass h = new HelperClass(this);
//does some work on a background thread, gets notified at some
//point in the future, through the callback, when the work
//has completed
longLivedComponent.doSomeBackgroundWork(new OnWorkFinishedListener(){
#Override
public void onWorkFinished(){
h.Addfiles(this, filename);
}
});
}
Now, let's also assume the following sequence of events:
User opens app, MainActivity#1 is created, its onCreate is called
doSomeBackgroundWork is called, background work is started. When the background work finishes, your helper (which has a reference to MainActivity#1) will have to do some work.
User rotates device, an orientation change occurs.
MainActivity is recreated by Android. MainActivity#1 is destroyed (i.e. onDestroy will be called), and a new MainActivity#2 will be created
Steps 1,2 will happen for MainActivity#2 i.e. doSomeBackgroundWork will be called again, from MainActivity#2. (strictly speaking, this step isn't really relevant, but nevertheless)
The Garbage Collector runs.
The background work started by MainActivity#1 at step #2 finishes.
Normally, at step #6 the memory occupied by MainActivity#1 would be freed, as MainActivity#1 was destroyed.
However, in situation described here, the OnWorkFinishedListener needs to be persisted by longLivedComponent until the background work finishes. The listener has a reference to both your MainActivity#1 (implicit anonymous inner class reference to its parent class), and to your HelperClass instance, which also contains an instance to MainActivity#1.
When the garbage collector sees this at step #6, it assumes that MainActivity#1 is still alive & used since there stil are objects pointing to it. Because of this, it doesn't reclaim its memory, you'll end up with two instances of MainActivity in memory and a leak occurs.
To prevent this, you could:
Use the application context instead of the activity context, in classes that will be used by long-lived objects. Also, in your case you don't need anything specific to the activity itself
Make sure that any long-lived objects (like longLivedComponent in our case), or objects shared between activities, won't store references to an Activity or its Context, after onDestroy() has been called on said Activity
Hope this helps
Related
I read many posts (one is this) and articles according to which you shouldn't pass context from an Activity to a non activity class, to avoid leak of memory.
I have to use the method findViewById() in a non activity class.
So far,I did this:
public class Text{
Activity activity;
public Text(Activity activity){
this.activity = activity;
}
public TextView getMyTextView(){
return activity.findViewById(R.id.textView1)
}
}
Now I'm wondering if even "passing" an Activity can lead to a memory leak.
What's the best way to be able to call, for example, the method findViewById in a non activity class.
The only thing which can lead to memory leak is misunderstanding of how GC works. The basic rule of avoiding memory leak is to make sure that objects which should be collected by GC are disconnected from GC Root:
There're set of GC roots, and one of them is static field. So, if you assign your activity to such field then GC could not collect it:
class ActivityRef {
public static Activity ref;
public static void setActivity(Activity activity){
ActivityRef.ref = activity;
}
}
And this activity(with all views, bitmaps and all connected objects) will remain in memory until the app is not killed by system.
Another GC root is Thread.
class SomeThread extends Thread{
Activity ref;
public void setActivity(Activity activity){
this.ref = activity;
}
}
While thread is alive GC can't collect activity instance.
Therefore there is no best way or method to call findViewById which allows you to avoid memory leak, there is memory model which describes how GC works, if you follow these rules you will never have memory leaks. More here.
Good articles about WeakReferences:
You don’t have to use WeakReference to avoid memory leaks
Finally understanding how references work in Android and Java
Rather then passing the activity reference pass it the views that are necessary. Make sure that the "Non-Activity" class reference is created after your activity class is invoked and release the reference (nonActivityReference = null) before destroying your activity.
I have a situation where my Android application crashes when it has been in the background for a while. To me it seems like it's because some of my objects are garbage collected. My structure and problem is the following
Object A is owned by the Android Application Object and created in the onCreate() method.
public class Application extends android.app.application {
private static Application instance;
private A a;
#Override
public void onCreate() {
super.onCreate();
instance = this;
a = new A(getApplicationContext());
}
public static A getA() {
return instance.a;
}
}
An ArrayList is created in the constructor of object A.
The ArrayList is populated with objects of type B via an async call (Downloaded from a server).
Now when my application have been in the background for a while (usually over night) and I resume it, it crashes because the ArrayList is empty. (ArrayList is never empty during normal use and definitely wasn't before I sent the app to the background (last night).
So I'm guessing that my B objects are garbage collected and I really don't understand how this can happen, since they are implicitly owned by the Android Application Object.
Does anyone hve any input on this?
You should probably override the onTrimMemory() and onLowMemory() callbacks in the Application class -- at least these will give you warning that you're about to lose your data
In my Android app I have set a custom Application-derived class. In it I have a member field to store some arbitrary object in.
So I have:
public class MyApp extends Application {
public static MyApp mInstance;
public Object mData;
#Override
public void onCreate() {
mInstance = this;
}
public void setData(Object data) {
mData = data;
}
public Object getData() { return mData; }
}
Now in one Activity I'm doing
public doSetData() {
someMyData = ....
MyApp.mInstance.setData(someMyData);
}
In another Activity I'm doing
#Override
public void onCreate(Bundle) {
Object myDataRetrieved = MyApp.mInstance.getData();
}
I can see that sometimes myDataRetrieved is null. However, I believe I have never passed null in MyApp.setData(). Of course, I can be wrong.
Yet, can there be such circumstances under which MyApp.mData becomes null by itself?
Yet, can there be such circumstances under which MyApp.mData becomes null by itself?
Sure. It will happen every time Android terminates the process, which will happen when your app is not in the foreground, Android needs RAM, and your app is next in line to be terminated.
just save your data and load it up on application object creation if the instance was destroyed, which will always be created before the activity.
if you minimize the memory use then it will survive longer in the android zygote before beig purged. saveinstancestate etc are good only for small data blocks. sharing data through app instance is ok and sometimes recommended, however it is not different from using static fields. same constraints apply.
if you want minimal memory use then using the parcels etc to save your data is not recommended, not even by google.
You can never guarantee that your data will be retained in memory, since the system can always reap the process to reclaim resources.
Thus, your application must always be prepared to save any persistent data either in onSaveInstanceState() (short term) or in onPause() (long term), and then retrieve it in onCreate().
That said, there's also the "singleton pattern" which I'm quite fond of. This is a software design pattern in which you create a special class just to hold your persistent data. There's only ever once instance of the class (thus the name "singleton") which is created on demand the first time your data is needed. The singleton is retained so that any subsequent needs for the persistent data just use the same object without the need to re-load the data from long-term storage. As long as the system doesn't reap your process, the data is always there with nearly zero cost to access. If the system does reap your process, the data is transparently re-loaded, and your application never notices the difference.
See https://stackoverflow.com/a/14779357/338479 for my implementation of a singleton.
well most of us familiar with this pattern:
public class MySingeltone {
public String mSomeReferenceTypeData;
public int mSomeValueTypeData;
private static MySingeltone mInstance;
private MySingeltone() {
}
public static MySingeltone getInstance() {
if (mInstance == null) {
mInstance = new MySingeltone();
}
return mInstance;
}
}
my problem is that I've found recently that the mInstance don't equal null after activity using him been destroyed, or when the whole application suppose to be clause, for example:
public class SomeActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MySingeltone mySingeltone = MySingeltone.getInstance();
mySingeltone.mSomeReferenceTypeData = "some value";
}
}
when launching "SomeActivity" next time after closing the whole applications running activities (say 10 seconds after..) the mInstance still holds the same reference, with the same values on his fields.
why does it happening?
what am I missing?
when android garbage collecting static members belongs to application?
Since "mInstance" is a static variable it will not get null when you close your application. Closing of application doesn't means that your application got destroyed.
Also there is no concept of Closing your Android app. If you get out of your app it will not get destroyed at the same time. Android OS handles it internally when to close the app when it is no more in use. In case of memory shortage when android decides to destroy the app then this static variable will also got null.
You can not control when exactly Java objects become garbage collected. An object becomes eligible for garbage collection when there are no more (non-circular) references to it.
With Android, further, you can not control when your Activity gets removed from memory.
why does it happening?
what am I missing?
when android garbage collecting static members belongs to application?
Ok first, as others said, there is no close application concept on Android as the android OS manages lifecycle of your application process on their own.
Second, you did the wrong test - if instead of closing all apps you would do the opposite - that is - fill up memory by starting more and more apps, then eventually your application's memory would be cleaned up to be used by other applications and this includes all static mebers as well as instance members! then, you will see that the static variable WILL BE NULL as you expected.
They just "lazily" clean up memory, if there's enough memory then you application might never get cleaned up.
Actually, there is no way around it, as far as i know, there is no way to grauntee an object would not be cleaned up at any point from the device memory. in somecases it leads to bad behaviour. such as if the singleton does heavy processing on its creation, calling getInstance might get your UI stuck, or even make your app crash due to irresponsibleness.
I'm confused on this. Just started android and have a long form that needs multiple activities to bring together an object. I would like to pass the object from activity to activity to build it. After reading the many posts and blogs and the Android Dev pages, it seems for non-persistent data, the best bet is to subclass application or create a singleton. I reviewed this post openFileOutput not working properly inside a singleton class - ideas/workarounds? and now my question is this, Why doesn't a singleton ever get recycled? If we createSingleton() in Activity A, then move to Activity B and we are never passing a reference to the singleton, how does the garbage recycler know that we are going to come back to it again? it seems to me that when Activity A is recycled and we have moved to Activity B that the singleton would die..
If we look at the following singleton..
public final class SomeSingleton implements Cloneable {
private static final String TAG = "SomeSingleton";
private static SomeSingleton someSingleton ;
private static Context mContext;
/**
* I'm private because I'm a singleton, call getInstance()
* #param context
*/
private SomeSingleton(){
// Empty
}
public static synchronized SomeSingleton getInstance(Context context){
if(someSingleton == null){
someSingleton = new SomeSingleton();
}
mContext = context.getApplicationContext();
return someSingleton;
}
public void playSomething(){
// Do whatever
mContext.openFileOutput("somefile", MODE_PRIVATE); // etc...
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("I'm a singleton!");
}
}
And we create an instance of it through getInstance(), the class places a single instance of the class into static field, someSingleton. Why is this instance never recycled? If the answer is, "Static fields are never recycled?" What keeps us from using up all of our memory if we have many of them? Simple design considerations? This seems risky if we are using lots of contributed libraries that we have no idea how many static fields are out there. I just have this feeling that there is some fundamental rule that I am missing in OOP as a newb.
The general pattern is to put a reference to your singleton class in a static field. Static fields are not tied to a particular instance so they stick around until the JVM process is alive. It doesn't really matter how many activities access it. If you need to 'recycle' the singleton, maye you don't really need to use a singleton? Or provide an explicit close()/open() etc. methods.
I think the reason that your Singleton's are not getting recycled is because activities in Android aren't destroyed when you think they are.
You pose a question like 'what happens when we move from activity A to B'. But when you do that in Android, Activity A is very rarely destroyed. It usually just goes into the onPause() state. Thus, your Activity A will still be (mostly) intact if and when a user decided to hit the back button enough times to get back to activity A.