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.
Related
I am using the adapter for ListView/RecycleView on my project. But I am wondering which kind of Context I should pass to the adapter? ApplicationContext or Activity Context(it's mean this on the activity)?.
As I know that the system does not kill the adapter even if the activity being killed. So I have some confusions here:
If I pass the Activity Context to the adapter, so the adapter have an implicit reference to the activity. Can the activity be killed?
In the other hand, I pass ApplicationContext. How long does the Adapter still live? Does it collected by GC after the activity be killed?
Which kind of Context I should pass in specific case?
Thanks,
If I pass the Activity Context to the adapter, so the adapter have an
implicit reference to the activity. Can the activity be killed?
Correction it is an explicit reference since you are passing it manually. So basically the answer to your question is likely YES because the one holding the Adapter is the activity itself, even if the adapter is holding a reference to your activity both of them will be garbage collected once the Activity is finished.
Unless you are dealing with Threads it is recommended to use WeakReference since a Thread can live longer than the activity itself.
In the other hand, I pass ApplicationContext. How long does the
Adapter still live? Does it collected by GC after the activity be
killed?
YES
Which kind of Context I should pass in specific case?
Both can work but Application context is a lot safer.
As I know that the system does not kill the adapter even if the
activity being killed.
Something is not right in the code, probably you are dealing the wrong use of statics or Threads. Additional code required here or a proof of your profiler!
ApplicationContext as it should get cleaned by GC when you destroy the activity if you have more than 1
While using RecyclerView, (which you should prefer to ListView), you most likely will implement RecyclerView.Adapter. One of the methods you have to override there onCreateViewHolder, provides you with the context you should use:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Use this context
Context context = parent.getContext()
}
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
im wondering how androids garbage collector works with data that is kept by the fragment (retain instance).
If I have a Class hirarchy like this:
class MyFragment extends Fragment {
private DataManager dataManager;
public MyFragment(){
setRetainInstance(true);
}
public void onCreate(){
if (dataManager == null)
dataManager = new DataManager();
dataManager.setView(this);
}
public void onCreateView(){
// display the data of the dataManager
}
public void onStop(){
dataManager.setView(null);
}
}
class DataManager implements DataChangedListener {
private MyFragment view;
private Data data;
public DataManager(){
data.setDataChangedListener(this);
}
public void setView(MyFragment v){
this.view = v;
}
}
class Data {
public void setDataChangedListener(DataChangedListener l){
this.listener = l;
}
}
So what i want to do is, that on orientation change the fragments view content will be recreatd. But the underlying data (DataManager and Data) must not reload.
The DataManager listen to the Data Object for changes, an will forward this changes to the UI, the Fragment. Fragment is "attached" to the DataManager when its (re)created.
So far so good. It seems to me a good Solution and well structured. Basically its some kind of Model-View-Presenter pattern.
But now im wondering, when Android will run the garbage collector to collect the DataManager and Data Objects.
Assume I have an Activity that displays MyFragment.
But what happens to the memory, when the user of the App navigates to another Activity, that displays something completely different.
Since there is a reference from DataManager to Data and vice versa, I guess that the data will be kept "forever" in memory, right?
So I guess the garbage collector will only remove this both when the device is going on low memory. But I guess, that these two objects would not be automatically the first two data objects that were garbage collected. I guess there is some kind of "memory deadlock".
What do you think? Any suggestions?
Regarding Garbage Collector:
I might be wrong on some concepts here but, as far as I know, the Garbage collector starts from a known object and "navigates" down the object tree, if any object is not on that tree, it will be collected.
So for example, it stats from the activity, and it have references to a few views, an adapter, and to the FragmentManger, the FragmentManager have reference to a 3 fragments, those fragments to a few data objects, and so on.
But if Object_A have ref to Object_B and Object_B to Object_A but no one else have reference to Object_A or Object_B, you can kiss good bye to those two.
Suggestion:
Usually to simplify the whole setRetainInstance(true); deal, I make a rule for myself (remember, that's not obligatory, but it does make life easier to understand and abstract): That if I want/need to retain data and will use setRetainInstance that one fragment does not have a view. That means, I do not override it's onCreateView and the transaction on it it's a simple add(mFrag, MyFrag.TAG);, not placing it anywhere in layouts.
That simplifies because you know that all the views are re-created when needed and you can always access your data using getFragmentManager().findFragmentByTag(MyFrag.TAG);
And back to the GC deal, with that approach you know that your data will not be GCed because it's safely kept by the FragmentManager.
sounds like a good plan? what do u think?
edit:
Further suggestion:
do not keep any reference to any View, or Activity, or Context in your DataFragment. Any long living object should not keep those references as they do hold the whole activity and that's a huge memory leak.
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.
In android, I need reference "context" or to say pass "context" to several classes. For this purpose I have implemented a static class which holds the "context" and all other class access context through it.
//let's say I have a static class DataHolder
//and my main acitivity i.e. whose context need to be refrenced
public class DataHolder{
public static Context context;
}
public class MainActivity extends Activity{
public void onCreate(Bundle bundle){
DataHolder.context = getApplicationContext();
}
}
So, is this process ok to be implemented or it is not the right way to reference in Android application.
The issue here is not about efficiency, but about the inherent risks of storing your context statically.
The context can change in many events, the most likely one is changing the device orientation, so you shouldn't relay on it always. I think you should pass the Context in the constructor to each class you think would use it (or, rather, redesign your code so you don't need it where it's not available, although this may be a bit tricky).
In the worst case scenario, you should try to update it as frequently as you can. But, then again, what's the point in having it always accessible? I think the risks are not worth the laziness (sorry if it sounds rude, I don't mean it to) of making a careful app design.
You should definetely avoid it, since it may lead to a memory leak.
Read: Avoiding memory leaks
This means that views have a reference to the entire activity and therefore to anything your activity is holding onto; usually the entire View hierarchy and all its resources. Therefore, if you leak the Context ("leak" meaning you keep a reference to it thus preventing the GC from collecting it), you leak a lot of memory. Leaking an entire activity can be really easy if you're not careful