I've been having this doubt since a long time, when I'm working with android fragments and I need to instantiate a Context, or I need to pass a Context as argument for other function, I can choose to use between getContext() and requireContext() methods to achieve that, I normally prefer to use requireContext(), but just because the IDE(Android Studio) shows warnings sometimes when I use the getContext().But the result of using one or other method seems to be the same, no matter what I choose.
My question is, is there any difference between these two methods? If this is the case, which one is better, or which should I use in each case?
getContext() returns a nullable Context.
requireContext() returns a nonnull Context, or throws an exception when one isn't available.
If your code is in a lifecycle phase where you know your fragment is attached to a context, just use requireContext() to get a Context and also keep static analyzers happy about potential NPE issues.
If your code is outside of regular fragment lifecycle (say, an async callback), you might be better off using getContext(), checking its return value yourself and only proceeding using it if it was non-null.
Being explicit with nullability is even more important in Kotlin where it is built into the language's type system.
While Laalto's answer is correct, I'm adding the code to prove the difference between requireContext() and getContext.
In Fragment.java you should see the following code.
#NonNull
public final Context requireContext() {
Context context = getContext();
if (context == null) {
throw new IllegalStateException("Fragment " + this + " not attached to a context.");
}
return context;
}
You can see that the requireContext() method throws an exception when the context is null.
#Nullable
public Context getContext() {
return mHost == null ? null : mHost.getContext();
}
On the other hand, If you see the code for getContext(), there is a chance that you might get a null value when called.
Related
I was reading fragment documentation and found this:
Caution: If you need a Context object within your Fragment, you can call getContext(). However, be careful to call getContext() only when the fragment is attached to an activity. When the fragment isn't attached yet or was detached during the end of its lifecycle, getContext() returns null
So my question is what is the best place to call getContext() inside the fragment. Like i can call it in onCreateView, or onCreate() or onAttach() on any other place.
I am asking this because recently I got a crash of null pointer using getContext in my fragment. So I thought I should create a global Context object and access it inside the fragment. But then I came across this text from official documentation so I am a bit confused what would be the best place to initialize this Context object.
It all depends what you need that Context for. Sometimes it's just fine to call getApplicationContext(), in other cases it may be needed to use what you are given in onAttach() or call getActivity() if you are in Fragment code. Some are also providing own Application subclass, exposing static method like getAppContext().
In any case, AVOID saving the context as it may lead to memory leak. Get it dynamically when needed only.
As a lot of wrong answers are given, I'll provide what's the best way to handle context inside fragments.
The best solution is checking if the context has a value whenever you need it.
You can do it by wrapping the code in which you access the fragment in 2 ways:
if (getContext() != null) { /* code here */ }
or, as stated in the documentation there's this method:
isAdded()
which: "Return true if the fragment is currently added to its activity." -reference
Again: please AVOID saving the context in a local fragment's variable.
You can do something like this in your fragment.
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
// After this point the context is initialized.
context=activity;
}
NOTE: I don't really get it why it is so not liked this answer.
First af all, depending on the version of android(which was not mentioned), of course the OnAttach is deprecated, it has to be checked.
Next:
I think that if you need cobntext somewhere, you can make a private or protected variable in Fragment, so the context is destroyed when it is garbage collected.
protected MainActivity activity;
Make sure you hold this variable dearly and its reference is not passed to other classes.
This should do the job.
You can implement your logic like this :
private Context mContext;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
#Override
public void onDetach() {
mContext = null;
super.onDetach();
}
When you required to use context,
if(mContext != null) {
//Add your logic here
}
I need context many times in my fragment:
...
account.restore(getContext());
...
dbHelper = new DBHelper(getContext());
...
DiskLruBasedCache.ImageCacheParams cacheParams = new DiskLruBasedCache.ImageCacheParams(getContext(), "CacheDirectory");
...
mImageLoader = new SimpleImageLoader(getContext(), cacheParams);
...
Toast.makeText(getContext(), "err: " + error, Toast.LENGTH_LONG).show();
...
RecyclerView.LayoutManager layoutManager = new CustomLayoutManager(getContext());
...
Or should I initialize it once and then use it.
What is best way ?
This is mostly a matter of preference. You can call getContext() wherever you need one -- no cause to be worried about perf overhead. Or you can assign a private Context context field in your onCreate method. Or, if a particular method has multiple uses, create a local variable.
If getContext was potentially slow, then you should definitely have stashed it, but it's really just a simple accessor (almost -- it does one bit of indirection internally).
Go with whatever you find most readable.
In this case, it would be best to call it once and use it. This is due to the fact that the execution will be faster without the extraneous function calls.
It would be common to see
Context context = getContext();
I have done this many times and stored it to a variable in a class. In that case though it looks like this:
class SomeClass {
Context context;
#Override
void onCreate(){
context = getContext();
}
}
I don't understand why View.getContext() returns null in this case:
#Mock
Context mContext;
#Mock
SensorManager mSensorManager;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
//...
#Test
public void initWithContext() throws Exception {
assertNotNull(mContext);
assertNotNull(mSensorManager);
when(mContext.getSystemService(Context.SENSOR_SERVICE)).thenReturn(mSensorManager);
ImageView imageView = new ImageView(mContext);
assertNotNull(imageView);
assertNotNull(imageView.getContext()); // Error because getContext() is null
}
First lines of View constructor:
public View(Context context) {
mContext = context;
Method getContext()
#ViewDebug.CapturedViewProperty
public final Context getContext() {
return mContext;
}
I am not mocking the ImageView, why View.getContext() returns null then?
EDIT
when(imageView.getContext()).thenReturn(mContext);
assertNotNull(imageView.getContext()); //Error, imageView.getContext() returns null
If these are pure JVM unit tests (i.e. run on your computer's JVM and not on an Android emulator/device), then you have no real implementations of methods on any Android classes.
You are using a mockable jar which just contains empty classes and methods with "final" removed so you can mock them, but they don't really work like when running normal Android.
See more here: https://developer.android.com/training/testing/unit-testing/local-unit-tests.html#mocking-dependencies
I guess your issue is what #wojtek explained, you're running local unit tests, you can mock something like using the context to retrieve some resource.
If you need to test your views behavior and mocking the Android APIs in the same time, I would suggest trying Robolectric framework
I am using the Factory interface on LayoutInflater to be able to set a custom typeface on all my TextView in my application. Unfortunately there are places in my application where it doesn't work. After investigation, it seems that in the adapter of my ListView for example, the LayoutInflater used to inflate the cells is different from the one that has been used fo the rest of the application (my activity has a complex structure with Fragments).
Is this behaviour normal ? How can I make sure that the same LayoutInflater is always used, whatever the way I retrieve it ?
Thanks
as You get reference to LayoutInflater from Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) (ViewGroup.inflate and Activity.getLayoutInflater are just convenient wrappers) i presume it always returns reference to the same inflater service, until it is destroyed and recreated, and then this newly created is returned, and so on... I presume that manager object aquired from getSystemService method are sth like "normal" service's binder objects.
EDIT:
And saying above i was wrong ;)
i checked out the source code and in android.view.ContextThemeWrapper (which is activitie's super class):
#Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(mBase).cloneInContext(this);
}
return mInflater;
}
return mBase.getSystemService(name);
}
and in android.app.ContextImpl which is probably mBase Context implementation:
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
where :
private static final HashMap SYSTEM_SERVICE_MAP =
new HashMap();
and ServiceFetcher is an inner class for caching and retrieving "system service instances".
What is sure there is one LayoutInflater per activity/ContextWrapper. For more observations study sources, please ;)
I don't think the quantity of LayoutInflaters really matters (unless you are literally thousands). But, yes, using multiple Inflaters is indeed normal. I use quite a few when working with my adapter views (as in I don't use one static one. Each adapter still only uses one).
If there are places in your code that the adapter doesn't work, you are probably missing something. I would post the code where the Inflater doesn't work.
What is the correct way to implement a constructor in android?
It seems that in an Activity or Service 'onCreate()' is where the magic happens.
The reason I ask is because I would like to be sure I'm doing the right thing declaring
attributes in the top of my classes (Context in particular) and then setting the attribute values inside onCreate.
// Activity launched via an Intent, with some 'extras'
public class SomeActivity extends Activity {
private Context context;
private String foo;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the object attribute for later use, good or Bad to do this?
context = getApplicationContext();
Intent fooIntent = getIntent();
foo = fooIntent.getStringExtra("foo");
}
private void someMethodThatNeedsContext() {
// For example:
Cursor c = this.context.getContentResolver().query(foo, xxx, xxx);
// Or is it better practice to:
// A) Pass the context as a local variable to this method
// B) Use getApplicationContext() locally when needed
}
}
Maybe either of these options is ok, and I'm over thinking it?
Any specific reading and/or suggestions you may have would greatly be helpful to me.
Yes, you are correct that initialization is supposed to take place in onCreate(). You don't really need neither to store a reference to a context, nor to call getApplicationContext(). Your activity is a context itself, so you just use wherever you need a context. For example, making a toast within an activity:
Toast.makeToast(this, "Some text", Toast.LENGTH_LONG).show();
Option B - Since you can call getApplicationContext() from any non-static methods in your Activity class.
In fact, Activity is derived from Context too (Somewhere in the inheritance tree..) so you can just do:
Cursor c = getContentResolver()....
You don't have to keep a reference to a context. Especially not static, that can cause problems.
And you are correct - since you usually don't create your own constructor for Activities, you put the code for construction in onCreate.
You are writing a method inside your activity, so you can call getApplicationContext() anywhere in your code, you don't need to use a local variable :
Cursor c = getApplicationContext().getContentResolver().query(foo, xxx, xxx);
Also remember that the activity itself is a context (the Activity class is derived from Context), so generally you can use this whenever you need to provide a context ( for example when creating an Intent : new Intent(this, ...)).