So this my inner class of a base adapter implementation. Note it is for an expandable list view but I use the same process of instantiation for all my list views
class GamesAdapter extends BaseExpandableListAdapter
{
private Context context;
public GamesAdapter(Context context)
{
this.context = context;
}
}
My question is , how do I dispose of this context because after some reading holding a context is a reference to activity so when I rotate the old instance may not be GC'ed thus a memory leak. Please help me
When the screen orientation changes the system will (by default) destroy the current activity and create a new one while giving you a chance to maintain its state. Cant you just set the context again ? im not sure if your handling the config change yourself.
me personally, I'd just pass in getApplicationContext() anytime i need a context since it does not depend on the activity lifecycle ( its not an activity context). But you can also get the context from a View in your getChildView() etc.
Further, if your list is going to be rebuilt on the config change anyway cant you just make its constructor take in the activity context you want ? that way it gets renewed ? So im not seeing where the leak would be yet.
If you rotate the screen the Activity is destroyed but also your BaseExpandableListAdapter implementation. So Android will create a new instance of the Activity and a new instance of your adapter, you don't have to worry about it.
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()
}
Have situation when you need to create components dynamically in the fragment. I also have a static functions to create a specific components that is used many times in the application.
Exist many opportunities to pass the context to the constructor.
But what is the best practice if the static function or when extends from the fragment?
I read a lot, but do not understand the entire picture. Thank you for your tips.
for example:
public void onAttach(Context context) {
fragmentContext = (FragmentActivity) context;
or
getActivity()
or
getActivity().getApplicationContext()
or
getActivity().getBaseContext()
or
getContext()
or... or.. or....
Thanks.
To create UI components, you should use Activity context. So that the Activity theme will be applied to the component. So you have 2 options in Fragment. One is saving the context at onAttach() and using that context to create components. Second one is using getActivity(). Both contexts are the host Activity context.
You should check whether the context is null, before using the context.
I have simple code snippet to implement custom list view.
In this code, I have CustomAdapter class which extends ArrayAdapter:
CustomAdapter adapter = new CustomerAdapter(getApplicationContext(),R.layout.listview_item_row, weather_data);
The constructor of CustomAdapter is as below:
public CustomerAdapter(Context context, int layoutResourceId, weather[] data) {
super(context, layoutResourceId, data);
mlayoutResourceId = layoutResourceId;
mcontext = context;
mdata = data;
}
If I write this, the logcat shows following error:
Java.lang.ClassCastException: android.app.Application context can not be cast to android.app.Activity
So I changed getApplicationContext() to this. and it worked fine:
CustomAdapter adapter = new CustomerAdapter(this,R.layout.listview_item_row, weather_data);
So, my question is : 1. why we can't pass Application context here? (for the customadapter). and
2. Which are the scenarios in which we must have to pass Activity context instead of Application context?
[Note: I have already read some answers here and here, but they do not concern with specific issue here. All are saying that 'you can use any of them', but in specific situation like this, it does not work. So, please do not mark as duplicate.]
You should pass the context of your activity while creating an adapter object. Application context is different from the activity context and they should never be interchanged. Using application context gives you the entire context for the application which depends on how you have set up your Application subclass is set up. While it can still compile, it may produce results that you do not expect. The reason for your crash is because of what has gone into your Application subclass and is likely specific to your case.
Simply put, adapter objects should use the local Activity context as this is the one that it is bound to.
Adapters should never get the Application context during initialization. As the link codeMagic mentioned, you should always pass along the Activity to a class object if it directly relates to the life of the Activity...of which adapters do.
Now, it's certainly possible to give the ArrayAdapter an application context. It won't crash or throw an error. However what it will do is render your views differently then expected. Mainly because the application context lacks the theming you may or may not have supplied for your App and/or specifically for a given activity.
As to why the crash occurs? Somethings specifically wrong with your code. My guess, you are defining mcontext as an Activity so its crashing in the custom constructor...or It's defined as a Context but being used somewhere that as an Activity context which is causing the crash.
In some places we were giving like "DatabaseUtil db=new DatabaseUtil(DailyPlanView.this);" where DatabaseUtil is the class with the constructor argument is context. But if we create the object for the DatabaseUtil class in the DailyPlanVIew class we are using the above code. My doubt is what is the use of the context and instead of passing the context object as argument why we are passing "this".
Whenever you are dealing with Context, its important to understand its used in everything. From using a database to obtaining system services. This is required by the way android works with Context. Specifically when you are passing this you are basically passing the class that encapsulates this statement.
class MyActivity extends Activity
{
onCreate(Bundle bundle)
{
View v = new View(this);
}
}
passing this refers to the object that encapsulates it. This is a Object oriented concept... Where this is reffering to MyActivity. One thing to keep in mind when passing context is ensure that you are passing the correct kind. Some Context objects have a longer lifespan than others and if not managed properly can lead to Context leaking. Specifically in this example, this works because Activity extends Context.
The differences occur in the View class.
getApplicationContext()
getBaseContext()
this, which in the Context of an activity has the life span of an Activity (Example above)
One thing to add about Context is that it is basically a reference to the current Application and it's specific data.
Some more information about context can be found in this thread:
What is 'Context' on Android?
I have some classes within my application that need to call Android functions that require the Context as a parameter.
I don't have it as the class is not a subclass of the Activity class.
What is the correct way to tackle this problem?
Pass it as a parameter on each call?
Pass it at class instantiation and keep it?
It depends on the role of the class. But anyway pass ApplicationContext but not Activity one. If you pass Activity context gc can't remove it from the memory when after you don't need activity anymore. But application context is used while application was not finished by OS.Refer Avoid Memory Leaks
Pass it as a parameter. Or better yet, get the application context to avoid memory leaks.
public class Example {
protected Context context;
public Example(Context context){
this.context = context.getApplicationContext();
}
}
I'm pretty much always going with a constructor parameter approach. I pass it in the instantiation and keep a private reference in the instantiated class.
You have to think about one important thing. If the class you pass the Context will exist longer than the Activity instantiating it then you should use the application context. If that class is doing UI stuff you will need an activity context.
Make sure that the class you are passing an activity context to won't last longer than the Activity or you'll leak the entire activity.
If you don't do UI stuff then go with the application context.
I pass it as a parameter, i think its de best form to do it
Pass it at class instantiation and keep it.
One typical example is when you create a db helper. See this link
I've answered this question here also.
You can do that using ContextWrapper, as described here.
For example:
public class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
super(base);
}
}
and use that class as it were Context
The best way is to follow Bean approach:
public class Example {
protected Context getContext() {
...
}
...
}
Then it depends on possibilities to access context. If class is fully independent then constructor parameter and private field seems best approach.
But that bean property way shields you from further code changes.