I am posting this question in the hope that I can get some kind of definitive answer.
Is it really impossible to access resources without an activity or context reference. Passing around such references when all that is required is to access some values or assets or strings which have nothing to do with the UI makes for overly complicated code.
Plus all those potential hanging references.
Also this completely ruins various Design Patterns such as singletons, having to supply parameters when getting the instance.
Putting a static reference
So is there a way or does the whole community just live with this problem.
Your resources are bundled to a context, it's a fact and you can't change that.
Here's what you can do:
Extend Application, get the application context and use that as a static helper.
public class App extends Application {
private static Context mContext;
public static Resources getResources() {
return mContext.getResources();
}
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
}
Your manifest:
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:name="your.package.path.to.App">
Make your Application class a singleton and use that. It is a Context. All resources are tied to your app, so you need a Context reference. You can pre-load strings, etc. in a singleton class if you don't want to load them dynamically.
I guess you could use context from some UI element like textView.getContext() if the other responses don't work for you.
Related
I am using different objects (for example a large Bitmap) in many activities, which is why I created a class called DataHolder that holds a lot of those shared objects as static objects.
However, I also use shared preferences a lot, which is why that data class also holds a singleton of a shared preferences helper class called Shop.
This in combination with the shared bitmap etc. leads to me having to pass on a context to every single method that uses the Shop! I probably have a context object as an argument to 50% of my methods, which is quite annoying.
Is it normal to pass on contexts a lot?
How can I improve my data structure?
You might consider having a global ApplicationContext which can serve your purpose for loading the DataHolder object. Usually I do not use such contexts, however, your situation makes perfect sense of using a global application context in my opinion.
To achieve that, you might consider having the following class in your project.
import android.content.Context;
import android.support.multidex.MultiDexApplication;
public class MyApplication extends MultiDexApplication {
private static MyApplication myApplicationInstance;
public static MyApplication getMyApplicationInstance() {
return myApplicationInstance;
}
#Override
public void onCreate() {
super.onCreate();
myApplicationInstance = this;
}
public static Context getAppContext() {
return getMainApplicationInstance().getApplicationContext();
}
}
And add this Application class to your AndroidManifest.xml, under the application tag like the following.
<application
android:name=".MyApplication"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<!-- Your activity declarations go here-->
</application>
Hence instead of passing the Context around, you might have a context supplied from a single space and hence improving your data structure. Hope that helps!
I've wondering if it's okay to do this: whenever we pass a Context variable around, could we just get a singleton reference from the Application class instead
For example here is a subclass of Application class with a single static variable pointing to its own instance
public class App extends Application {
public static mApp;
#Override
public void onCreate(){
mApp = this;
}
}
Then when we need to pass a Context to a method from somewhere, can we just do
foo(App.mApp);
Isn't it okay to treat Context as an application variable?
Well it depends on the context in which you are using it. Many times a context is meant to keep hold of things until it's lifescope is complete and then allow garbage collection to take back whatever it was owning.
Other times the context needs to be an activity to handle life cycle call backs such as onNewIntent or onActivityResult.
Keeping a static instance in the parent is just a shortcut to avoid having to getApplication() and cast it as your type of application. I typically make a method for MyApplication.getApplication().doSomething which will return it's own reference of itself as opposed to ((MyApplication)getApplication()).doSomething
Just seems cleaner for coding purposes. But I would be very leary of using the application context everywhere you need a context, it will come back to bite you eventually.
But yes you can certainly store yourself as a static variable to be shared, I do it in most applications, but typically for a specific shortcut purpose of clean maintainable code, not for cheating on getting context from various crevices.
I'll try really hard to turn this into one comprehensive question:
I'm writing a method to get a String that contains the name of an Android device's city, as determined by the LocationManager and getLastKnownLocation() and all that.
Then I realized I'd need to do the same thing again in another activity, so why not just make an entirely separate class (LocationFinder) that I could use across my program, instead of writing duplicate code everywhere?
But I've run into problems that confuses me. For instance, if I make this class (LocationFinder), should it extend Activity, even though it is never actually visualized? All this class would do is have a variety of getters like getLastKnownCity() or getCurrentCity() and return strings. I assumed it wouldn't HAVE to extend the Activity class, since it's really not an activity.
But then what Context do I use for:
Geocoder geocoder = new Geocoder(Context context, Locale locale)
?
This made me assume it MUST be an activity. So I extended Activity, and replaced the constructor with
#Override
protected void onCreate(..............
but for some reason, that never ends up getting called, even when I put
String city = new LocationFinder().getLastKnownCity();
My very first line of LocationFinder's onCreate() is
System.out.println("HEY!")
and it never even gets to that. I get a null pointer at android.internal.os.LoggingPrintStream.println() and other stuff.
Plus, there's a bunch of system constants that come from Activity classes. For instance, I need to get at LOCATION_SERVICE, which is a String, which I can't get without extending Activity. Sure, I could cheat and just put in the literal string, but that feels wrong.
EDIT: If possible, use frogmanx's answer. This should only be used when his answer is not possible to use. (ie. singletons that need a context right off the bat.)
Sounds like you should extend Application and not Activity.
Make your Application something like this:
public class MyApplication extends Application {
private static MyApplication instance;
public MyApplication() {
instance = this;
}
public static MyApplication getInstance() {
return instance;
}
Then add this attribute to the application tag of the manifest:
<application android:name=".your.package.MyApplication" ... />
After all that, you can get a Context by calling MyApplication.getInstance() from anywhere.
When constructing your class, you can have a constructor that takes in a Context and assigns it a local Context object within your class.
public class LocationFinder {
private Context myContext;
private Geocoder geocoder;
public LocationFinder(Context context)
{
myContext = context;
geocoder = new Geocoder(myContext);
}
}
And then when you try to access this class, make sure you initialise it like:
public class TestActivity extends Activity {
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LocationFinder lFinder = new LocationFinder(getApplication());
}
}
Of course, you can't access a context from every class that you will be running. So a reference to a View can suffice.
LocationFinder lFinder = new LocationFinder(anyView.getApplication());
should it extend Activity, even though it is never actually visualized?
No. From the Android docs
An activity is a single, focused thing that the user can do. Almost
all activities interact with the user, so the Activity class takes
care of creating a window for you in which you can place your UI with
setContentView(View)
Think of an Activity as a screen the user sees.
But then what Context do I use for
Geocoder geocoder = new Geocoder(Context context, Locale locale)
The Activity class extends Context, as do a lot of other classes including Application. A context provides access to resources associated with the class which extends the context.
You only need, and should only use, an Activity context when required to interact with resources associated with that Activity and methods implemented by the concrete Activity class. When you do need that access to that context, then you would pass it to the class needing access, typically as an argument to a constructor of that class.
If you ever do pass an Activity context outside of the activity extending it, make sure that the scope and lifecycle of the reference is less than or equal to the extending activity otherwise you will leak large amounts of memory if the activity is destroyed since the garbage collector cannot free the memory since there is a reference to the context.
If you take a look at the constructor for Geocoder you will see that it takes a Context as an argument, as you know. There is a clue as to why the Context is needed in the description:
Geocoder(Context context, Locale locale)
Constructs a Geocoder whose responses will be localized for the given Locale. [1]:
The reason the Context is required is to gain access to system information about the platform locales and the current system locale.
So in your example, you could simply pass the Application context to the constructor, which you can get a reference to with getApplicationContext()
For instance, I need to get at LOCATION_SERVICE, which is a String, which I can't get without extending Activity
You can get it from the application context.
I have an application which need to access context in a lot of different classes all the time, for saving and serializing data, showing dialogs etc.
According to an article on the Android developer site, this causes memory leaks:
http://developer.android.com/resources/articles/avoiding-memory-leaks.html
What is the general approach for accessing context? Should a create a singelton class which holds one reference to context as soon as the app is started or what's the best approach?
Right now my methods look like this for instance
public void saveData(TheCassName classObject, Context context){
//do some stuff that involves context
}
And is called from wherever i need it.
Thanks!
Just to clear: There is no memory leak as context which is getting saved, is part of the application which is a process which will only get killed when the application will close.
Extend application in your app and then use the application context by
making static variable in that.
public class MyApp extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApp.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApp.context;
}
}
You need to define application also in your manifest too.
<manifest>
<application android:name="com.abc.MyApp">
</application>
</manifest>
Try using application context instead of activity context. However, there are limitations on app context you should be aware of: When to call activity context OR application context?
I have been working with android for a little while now and feel pretty comfortable with the platform, but I have gotten a little confused with the Lifecycle of Context Objects. Looking at the hierarchy it is easy to see that Activity and Service both extend Context, and while this is convenient, it is concerning. I have avoided making helper classes that need a shared resource have a static field holding a context (since just about all resources come through some interaction with a Context object) so that way when an activity is destroyed, the GC is free to free it at any time, but I am wondering about resources fetched from a Context.
For example, if I have a static field that holds a File inside of a class. Then make this class's constructor take the current context and assign the File a File resource fetched through the Context passed in, the do nothing else with the Context in my 2ndary class, am I still holding on in some way to the Context?
class testClass{
private static File someFile;
public testClass(Context context){
synchronized(testClass.class){
if(someFile!=null){
//even though I am holding a File, or a SharedPreference Object generated from this context, am I correctly preventing this utility class from holding the Activity object in memory for no reason?
someFile = context.openFileOutput("Some_File.txt", Context.MODE_PRIVATE);
}
}
}
}
I did just read about Context.getApplicationContext() (Sadly not static). It says it returns a context relative to the process and not the activity so if I need to keep a context around, use that one. But the question above still remains.
I remembered I asked this question and thought I would answer it.
Though there may be more kinds of contexts, the primary ones developers use are the Activity Context, and the Application Context (and other things like Service Context). The Activity context is created and destroyed with the activity, so it is not a good idea to use as a constant reference stored between activity creation and destruction. The Application Context doesn't have some of the things an Activity Context has, but everything you would want a static context reference for is there (file IO, preferences...). The application context is also created and destroyed with the application, so you can guarantee that as long as your application code is running, the context is valid.
Because of this, the Application context should be used for things like worker threads that may need a constant access point to a context but not need access to an activity. The best way I have learned to do this is to extend the android Application class. This class is created when the application is created in memory, and as soon as the Application onCreate method is called, the Application Context is valid. This means you can create a static function in your custom application class that gives access to the context.
public class CustomApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public Context getAppContext() {
return context;
};
}
The only other thing you need to make this work is a modification to your manifest file so android knows to use your application class instead of the default.
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:name=".CustomApplication" >