Let's use custom or extending View as an example.
Is it more effective to save Context parameter from constructor as a field, than calling getContext() everywhere (supposing there are, let's say, 10 or more places where it is needed)?
Instead of using getContext() every where, it is better to pass current context as argument in constructor where you wanna to use.
View#getContext() is
class View {
protected Context mContext;
public final Context getContext() {
return mContext;
}
}
and a locally cached implementation:
class X {
private final Context mLocalContext;
public X(Context ctx) {
mLocalContext = ctx;
}
}
Now there is a very small difference when you use mLocalContext instead of getContext(). The JVM can get to the required reference of the context object without having to execute the method (which takes a tiny bit of extra time). That call can't be optimized away since View#mContext is mutable (can change). In the local example it can assume that mLocalContext can't change and optimize the code a little better. [Note: I am not 100% sure about what optimizations are / can be done]
The difference might be measurable if you use the context a lot but in this case it does not matter much. It's still a good idea to cache Objects locally if you need them often. Especially when their (re)construction takes time (e.g. when getContext() would create a new Context() or so).
It looks like, from the source code, the View's constructor stores the context parameter and that's what getContext() returns:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/view/View.java#View.getContext%28%29
So, my instinct would be that storing the parameter yourself would be redundant, since the super class is already doing it for you.
Related
This question already has answers here:
Non-static variable cannot be referenced from a static context
(15 answers)
Closed 3 years ago.
Here is my method:
public Cursor rawQuery(String sql, String[] selectionArgs) {
try {
return m_db.rawQuery(sql, selectionArgs);
} catch (SQLiteException e) {
reportException(Context.getApplicationContext(), e);
return null;
}
}
Android Studio (3.5.3) complains saying
Non-static method getApplicationContext() cannot be referenced from a static context.
I don't see any static context here. rawQuery is a perfectly good class method of a class which is a wrapper around SQLiteDatabase to save me having to check for exceptions on every call. The exception checks are needed because it's accessing a public database and some other process may have modified it in such a way that my operation fails (for example by dropping one of my tables). It is (currently) only created and its methods called from an Activity. Of course I could pass the calling activity's context, but this is clumsy since it isn't needed most of the time and IMHO it's very poor programming style to include extra arguments in the methods of a wrapper class.
reportException does what its name says and writes a log file or displays a Notification or a Toast, depending on circumstances, and all of these need a context.
I've seen suggestions on the net to create a static instance of a subclass of Application to cache the application context. As commenters have pointed out, this doesn't always work if you need the application context in a class constructor (or anything which is called from one), but I don't expect to do this. My wrapper class is created as needed when I want to access the database. However I'm not sure if tha Application subclassing trick works if I open a database in a background server which may get kicked out of memory when not active and later restarted by the OS. It may be that the only solution is to cache the creator's context in the constructor of the wrapper class: this only requires passing the context once. However I don't much like the idea of keeping a copy of the passed context: it looks inelegant and a potential problem with garbage collection since I have to take care not to use the cached context when creating anything persistent..
However I still don't see Android Studio's justification for complaining in the case shown. I tried removing all the calls to rawQuery and it still complains, so it isn't walking the call tree to look for a non-static context. It looks as if it may be complaining if getApplicationContext is used in any class which isn't a subclass of Activity, which certainly isn't justified.
I don't see any static context here.
The "static context" referred to by the error message is the way you are calling the method: Context.getApplicationContext(). Since you are using the Context class name, this counts as a "static context". You need a Context instance in order to call getApplicationContext().
Of course I could pass the calling activity's context, but this is clumsy since it isn't needed most of the time and IMHO it's very poor programming style to include extra arguments in the methods of a wrapper class.
Yes, I agree that you should keep your argument list as trimmed down as possible. You say that this method is a wrapper around SQLiteOpenHelper which requires a Context as one of its constructor parameters. So presumably your own constructor takes a Context to pass to the wrapped SQLiteOpenHelper instance. One solution is to keep that Context as a field in your class. Then you can just use this.context.
I have several classes in my application that uses the Context object to access SharedPreferences and serialize files. Simply put, I want to know how to "design away" the Context.
The background to why I want to do this is because:
The classes should be created in the onCreate() method of a Fragment (and the Context is not decided at this point)
It's just plain ugly to pass around the Context all the time. Especially since I use Singleton-reminding instantiation of these classes (Don't judge, please)
The specific context isn't really needed here, so it should be possible to design away... (What I mean is that I only need the Application Context)
An example of why this is ugly is my Cache object. It holds cached values downloaded from 1-5 different sources decided at runtime.
public static Cache getInstance(Context context) {
if(instance == null) {
instance = new Cache(context);
}
return instance;
}
When later using this object, it needs to read a SharedPreference which needs the Context, so it has to be passed around every single time I want to get an instance of the Cache.
So how can I get rid of these ridiculous contexts? Using the Application Context should be just fine... I guess that the problem can be boiled down to something like "How do I get a SharedPreferences object" in an object without a specific Context?"
I guess that the problem can be boiled down to something like "How do
I get a SharedPreferences object" in an object without a specific
Context?"
Using the Application Context. For this purpose you can subclass Application, registering it in your AndroidManifest file, and have a method to retrieve it from every where, like a singleton
I have seen the static getContext() method on the Application object before and I think it's slightly ugly and I wasn't sure that it was "Risk free" and correct. I was just about to implement it when I found this: https://androidcookbook.com/Recipe.seam?recipeId=1218 which basically says that the Application object in Android can be treated as a Singleton and that I should place my own Singletons inside that object.
It's essentially the same as #Blackbelt 's solution, but gives a slightly nicer vibe!
I have two activities, a MainActivity and a secondary activity (e.g.: an about screen), then I have an asynctask which updates the UI on the MainActivity. This part works fine, the asynctask updates the UI by calling a method inside the MainActivity which inflates the UI and sets some values. This method also makes all UI components visible.
What doesn't work is, after going to the About screen and back to the MainActivity, the UI is completely blank. I don't understand why this stops working after navigating back from a different activity, which otherwise works fine.
Can someone please advise?
Here's how I draw the UI. This is how I update it from the thread, and it works, until I go to the about screen:
private void DisplayMainContent()
{
Context context = Util.DataStruct.LoadContext();
Log.d("debug", "DisplayMainContent() loaded a context " + context.toString());
RelativeLayout parent = (RelativeLayout)((Activity)context).findViewById(R.id.action_settings);
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = li.inflate(R.layout.activity_main, parent);
TextView version = (TextView) v.findViewById(R.id.latestVerField);
version.setText(Util.DataStruct.GetVal("version"));
}
little story about vanishing data..
advice:
do not use new activity to achieve this - do your about as dialog or dialog fragment
nice example how to show dialog using fragment
don't use static - instead use singleton pattern
Singletons preserve the conventional class approach, and don't require that you use the static keyword everywhere. They may be more demanding to implement at first, but will greatly simplify the architecture of your program. Unlike static classes, we can use singletons as parameters or objects. Also,you can use singletons with interfaces just like any other class.
where i see problem:
this line is all u need to trace yr mistake:
(i think any other fragment of yr code is irrelevant to yr problem)
version.setText(Util.DataStruct.GetVal("version"));
Explanation why:
Util.DataStruct:
should be singleton with valid hard reference to it eg. in Applictation class or any other which life is longer as the activity u use to display data.
are you aware of the existence of garbage collector?
what i'm trying to point out ? why u should avoid STATIC !?
Code(data) flow:
app launched - initializes static class/variables etc
your variables are feed (via async or else way)
your app is closed by ANDROID OS - regardless of the reason
os recreates "stack"
but not yr variables - they are empty/null/defalt - not referenced by values as they shoud in normal code flow
context:
from where do u use yr DisplayMainContent() ? for what u need context there ?
context should be "anchor" for yr app methods which need it. (it's like certain security stuff - "hi this app fragment belong to me i have the right to modify and view contents - so to do any stuuf u pass nearest context u got - from fragment activity dialog widget etc")
if u can use getContext() eg. ("from parent") - dont use any static one
example:
in fragment:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context context = container.getContext();
}
in adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Context context = parent.getContext();
}
about inflation
- use :
LayoutInflater.from(context).inflate(res,ViewGroup,attachToRoot);
do u use parent in inflation(in fragment doubtless u use in activity doubtful)
for #bcorso:
Do not use more resources than you need.
#TomaszBest sorry, but you really don't know what you're talking
about: Util.DataStruct.GetVal() is calling a static method of the
static class Util.DataStruct, and therefore must return a static class
variable. Static class variables are singletons (only one will ever be
created), and it will not get garbage collected.
An object referenced through a static member variable is strongly referenced until the class is unloaded. A normal ClassLoader never unloads a class, but those used by application do under the right conditions.
If the static field is changed to reference a different object, the original object pointed to by the static field is eligible for GC just like any other object!
The initialization of static variables is covered in Section 2.11 Static Initializers of suns JVM spec. The specification does not define the implementation of Garbage collection - garbage collection rules for static objects will vary depending on your VM.
in sum:
If your class is holding onto this object permanently, it will only be released when the vm exits. Only Classes and interfaces loaded by the bootstrap loader may not be unloaded.
From my understanding, Application in Android is a singleton (correct me if I'm wrong) and we always have just one application Context instance.
So, from this perspective, is it a bad practice to save the application Context in my Application class?
Can it lead to a massive memory leak?
Here is an example:
public class MyApp extends Application {
private static Context appContext = null; // <-- here is the thing!
#Override
public void onCreate() {
appContext = this;
}
public static Context getApplicationContextSingleton () {
return MyApp.appContext;
}
}
The reason to do this is globally accessed classes, like PreferencesManager, that mostly have static methods always need a context. So, instead of passing it everytime (or even storing it in an instance, which can be bad), I thought about storing the app context. What are the drawbacks I'm not seeing?
is it a bad practice to save the application Context in my Application class?
It is a code smell.
Can it lead to a massive memory leak?
Having the static data member will not lead to a massive memory leak. Whether your over-use of the Application object will lead to a massive memory leak depends upon where and how you use it.
What are the drawbacks I'm not seeing?
Not all Contexts are created equal. Generally speaking, only use Application when you know specifically why you need the Application context, not for everything.
Dave Smith of DoubleEncore has an awesome blog post covering the differences between types of Context and when to use one over another.
Why do I get the error message The method getResources() is undefined for the type ColorObjectManager?
I use this line to load a Bitmap image:
orange = BitmapFactory.decodeResource(getResources(), R.drawable.pearl_orange);
It's working fine if I'm doing this in another class that I call GameLoop which I make an object of inside the MainActivity class. But it's not working when I trying to do this in the class ColorObjectManager which I make an object of inside the GameLoop class. Do you follow?
Why am I limited to just use this loading part in the GameLoop class and not in the ColorObjectManager class? I thought it would help if I passed the Context to the constructor of ColorObjectManager, but it didn't! I guess I'm missing some knowledge here where I can create objects and not. Can I get some help to sort this out? Thanks!
getResource needs a Context object. If you pass the context to ColorObjectManager you can retrieve resources with context.getResources()
I agree with the answer posted by blackbelt. Pass the activity context to the constructor of ColorObjectManager from your activity class.
new ColorObjectManger(ActivityName.this);
Constructor
Context mContext;
public ColorObjectManager(Context context)
{
this.mContext= context;
}
Then use the context to get resources.
Edit:
If you want to use the context only in your load method
public ColorObjectManager(Context context)
{
load(context);
}
To get access getResources() , Activity context is required. Your ColorObjectManager is not an Activity. So you need to pass the Activity context to this class.