In my last project in the activities I had a lot of MyActivity.this provided to the methods requiring context, so I decided to make it like this in the beginning of the class
private Context context = ActivityStage2.this;
and then just pass context to object methods. So far it works ok, but is it ok at all to declare the Context like that? I mean does that always have the updated state of the MyActivity.this each time the context object is referenced?
You can do it but there is no point in doing it.
You basically "cache" the this reference to a field. The field initialization will run just before the constructor body, so every time the object is recreated the reference context will be updated. This works similarly to this, which points at this instance object.
Now why would you do that? Readability? It seems like you want to use it in inner classes, because you explicitly qualify it with: ActivityStage2.this. The java way to qualify the outer class reference from an inner class is well understood by programmers, and here the gain is little. One exception could be anonymous inner classes where you don't have the name of the inner class. In this case caching the reference to the Activity makes sense, but I'd rather use a final local variable instead of a field.
Another reason for doing this could be that you need only the Context interface instead of the ActivityStage2 interface: that makes sense theoretically but in practice I wouldn't do it without some other better reason.
Last thing: if you turn your field in a static one, you will indeed leak.
There is no problem to do this if you follow the two rules listed bellow:
Keep the reference in a instance member. If you use a static field, your Activity instance could never be recycled by the GC because your MainActivity class has a strong reference to the object. If you really need to do this, use a WeakReference.
Keep a reference to your Activity instance. Do not keep a reference to other activity due to the reasons I have just described above.
Otherwise it all depends on you and your code style :)
Related
Over the development of an Android app I've come to a collection of utility-type methods that I have put into a static class. All these methods are used across multiple Activities and most of them do not require any information from the calling Activity.
However, I now have some methods that require the Context of the Activity and one that requires the Activity itself. Let me exemplify some of them:
getDeviceNaturalOrientation() - Uses an Activity's
getWindow().getWindowManager().getDefaultDisplay() to access the
displays rotation, width, and height to determine the device's
natural orientation.
getDeviceOrientation() - Similar to the above but to get the current
orientation
createFile() - Uses the Context to to access some resources (strings) and to
create and show some Toasts
Now, my big questions regarding this Utils class are:
So far, each function takes a Context parameter which I pass from whatever Activity I'm on, but would it be OK to create a static Context or Activity variable in the Utils class and set it at the beginning of each Activity (like in onCreate)? This variable would be used in whatever functions require a Context or Activity instance.
Assuming the above is not recommended, is it OK to pass an Activity parameter to a method or is there a reason to only pass an Activity as Context? The methods I use for the device orientation functions above are specific to Activity objects, not Context, so either I pass as Activity or pass as Context and cast into Activity (the latter sounding like a terrible idea).
Also, I am very open to the idea that this Util class may not be the way to go for these methods that require Context/Activity, so I would welcome alternatives that would still prevent having copies of these methods in each activity class that uses them.
1)A static link to a context is likely to cause a memory leak. It means that a reference to the Activity will be kept around in the static variable even after its destroyed, so all of the memory of the activity and all its views will remain valid and not be cleaned by gc. You can do this, but you have to be careful to null out the variable when done. Its better just to avoid it.
2)Its a little bit awkward to pass the activity as an Activity, but no technical reason not to. At that point we're arguing over code cleanliness/maintainability. And there are times where the non-clean solution is just easier. Of course in the cases above I'd rather pass the orientation/display/Resources objects to the function than pass the entire context or make special accessors.
I think following design should be fine when you call from Activity
MyUtility utility=new MyUtility();
utility.getDeviceNaturalOrientation(this);
utility.getFile(this);
And you can define these function like
public int getDeviceNaturalOrientation(Activity activity){
//code
return some_oreientation
}
and like this
public File getFile(Context context){
//code
//return file handler
}
Activity is the subclass of Context so you can even change the design to following
MyUtility utility=new MyUtility(this); //this refer to Activity
utility.getDeviceNaturalOrientation();
utility.getFile();
As long as you pass activity you are fine but if you do following from your activity you will get error from first method call
MyUtility utility=new MyUtility(getApplicationContext());
utility.getDeviceNaturalOrientation(); //will throw exception
utility.getFile();
And, yes first idea is not a recommended way.
I would suggest you to send a WeakReference of your Activity or getApplicationContext() (for those works which can work using it) and don't use static method because it cause memory leaks. Read Developer blog also
Somewhere in the application, I need to get a localized string using the getString method for an error message. For this, I need a Context instance, gotten from for example an Activity. Is this really how this is designed? Am I really forced to pass around these objects into classes and methods or am I missing the point and is there some other way to get a string reference?
To clarify, in an Activity I have an inner AsyncTask subclass that in doInBackground instantiates a new class for some short network processing outside the UI thread. I want error messages to be localized and for that I need to pass in a Context instance (in other words, the Activity) into that class. The design of getting value resources from the XML files just seems to be a bit unintuitive. It makes me wonder why this is so coupled together with Context instances and not something static or - forgive me - a singleton, as Context implies to be the global application context and not just a part of it like an Activity.
No, you should not do this. A simple rule is; if what you need the context for is touching the UI or is only associated with the internals of the activity class, then you should use the activity context. Even then, it is important that any reference to the context does not have a lifetime which is greater than that of the activity.
The big danger of not following this is that you pass out a reference to the activity context to somewhere deeper in your code and your activity is destroyed whilst the reference you are holding is still in scope. You just leaked your activity and everything it has a reference to. I would recommend not passing the activity context outside the activity unless truly essential and even then, be very sure to control that life time.
So, it the context is needed for something which is not UI related, such as your need to get a string resource, then use the application context. Inside an activity, and where the string reference is declared in the activity, then using the activity context would be acceptable and in my opinion, preferred as you are making a conscious decision regarding scope and life time.
That said, you should ask whether this particular method is better placed in an activity. It may well not be but do ask yourself.
Finally, a small pedantic point. You do not pass objects anywhere. You pass a reference, in fact a value of a reference to the object. Everything in Java is passed by value.
You could always extend the application class. Make a static method on there for getInstace() to get the context.
And which way is better?
Let me know if the question needs clarification.
For reusing passing the context is easier as you then can simply copy it to another project. Otherwise you have to change all the MyActivity.this to OtherProjectActivity.this.
But most of all it doesn't matter what you use
I have chosen to answer my own question, not because any of the answers are bad, but because while they are equally good, none provide a complete answer.
It seems that one of the main factors to take into consideration is reusability:
Using MyActivity.this to refer to the context means that you will have to modify your code if you ever decide to use the class in another project/class/context.
Passing the context in the constructor and referencing it as a private variable, allows you to reuse the class wherever you want without modifications.
Another factor that will influence your choice is whether your inner class is public or it is private. It doesn't make sense to make an inner class public and then reference the context with MyActivity.this. The application would force close the moment you use the class from another activity. I would argue though, that a public class belongs in its own file, but that is up to the individual developer.
Lastly there is the matter of simplicity, as it is simpler to write MyActivity.this than to implement a constructor etc. This seeming simplicity can come back and bite you, as you can see above, if you decide you need to use the class somewhere else.
I will continue to use MyActivity.this out of simplicity for all inline eventhandlers, but for any other situation it seems that passing the context to the constructor is best practice.
This is more of a design issue as having a reference either way will have the same result. Consider the complexity, the access level and other design elements related to the inner class.
I have a helper class that I need context so I can access the SharedPrefences. Other posts recommend passing in the application context on instantiation of the helper class. So I made that change, it works very well except within a tab activity. The tab activity need to call a webservice to determine what data to display. The helper class makes the webservice call.
You can call getContext() from any activity. If the helper class is defined as a subclass of the activity, it can call it directly. Otherwise, passing the context through instantiation would be my second choice. I agree, it's not pretty passing contexts everywhere. There are probably some complicated OOP patterns you could use to avoid this, but I can't see it being an advantage overall.
If you get a null pointer you might be calling the function too early. In what function are you calling it?
I have created an ExpandableListAdapter class and I need to send it the context from the activity that is accessing it.
MyActivity.class:
MenuExpandableListAdapter.useInstanceContext(getApplicationContext());
MyExpandableListAdapter.class:
static Context context;
public static void useInstanceContext(Context applicationContext) {
context = applicationContext;
}
The above code works, but this also works:
MenuExpandableListAdapter.useInstanceContext(this.getApplicationContext());
What's the difference? Is this a good way to pass context? I'm still trying to fully understand context.
Context is necessary in order to get access to the resources and some other things. So, both - application and activity contexts work. But a good practice is tight to the smallest thing, which works, which is activity in your case. So, I would suggest new way for you:
MenuExpandableListAdapter.useInstanceContext(this);
Also, in your example, there is no difference between the calls. this is just the reference to the current object.
this refers to the object that is currently executing code, if the method is declared in the same class, and is not static, it is the same to call:
getApplicationContext()
and
this.getApplicationContext()
(The same applies to class members)
MenuExpandableListAdapter.useInstanceContext(getApplicationContext());
The getApplicationContext() method will called by using calling/current object implicitly.
in second you are giving calling/current object explicitly because this is special object that is always refer to calling object.
i suggest you to use this
MenuExpandableListAdapter.useInstanceContext(this.getApplicationContext());
because as per my reading it's good practice.