Should Context be injected with Dagger? - android

I know it is possible to inject Context with Dagger. We can see examples here and here.
On the other end, there are numerous posts about not placing context on a static variable to avoid leaks.
Android Studio (lint) also warms about this:
Do not place Android context classes in static fields; this is a
memory leak (and also breaks Instant Run)
I understand that by injecting a Context with Dagger, we are placing it on a singleton class, so context is somehow static. Doesn't this go against the lint warning?
Injecting the context seems to create cleaner code, since you don't have to pass it to several classes (that don't need it) so that they can further pass it to other classes that need it for some reason (getting a resource for instance).
I am just concerned that this may cause some undesired leak or breaks lint in some way.

You should never store/reference activity context (an activity is a context) for longer than the lifetime of the activity otherwise, as you rightly say, your app will leak memory. Application context has the lifetime​ of the app on the other hand so is safe to store/reference in singletons. Access application context via context.getApplicationContext().

If you are aware of Android lifecycles and are careful to distinguish the Application Context and the Context of Activities and Services then there is no fault injecting the Context using Dagger 2.
If you are worried about the possibility of a memory leak you can use assertions to prevent injection of the wrong Context:
public class MyActivityHelper {
private final Context context;
#Inject
public MyActivityHelper (Context context) {
if (context instanceof Application) {
throw new IllegalArgumentExecption("MyActivityHelper requires an Activity context");
}
}
}
Alternatively you could use Dagger 2 Qualifiers to distinguish the two so you don't accidentally inject an app Context where an Activity Context is required. Then your constructor would look something like this:
#Inject
public class MyActivityHelper (#Named("activity") Context context) {
Note also, as per David's comment, a Dagger 2 #Singelton is not necessarily a static reference.

Related

Am I injecting context safely? Dagger, Android Studio warning

From what I understand reading other answers here and researching, injecting the Application Context into a field should be safe from memory leaks, whereas holding Activity Context in a field would cause a memory leak.
I am injecting Application context with Dagger like so:
AppModule:
#Singleton
#Provides
fun provideContext(application: Application): Context {
return application
}
ViewModel:
// Injected context provided by Dagger
#Inject
lateinit var mContext: Context
Android Studio still throws this warning on the injected context field:
This field leaks a context object
Is it actually leaking a context object, or is Android Studio just not able to determine that its the Application Context that gets injected and I should ignore the warning? Do I need to inject it as a weak reference? Thanks.
Technically you can't leak the Application context, because the application context is available as long as the Application is alive. (Kind of self explanatory).
Easiest way would be to ignore the warning, it shouldn't get you in trouble.
If you still want to fix the warning, you could inject a WeekReference of the context. (WeekReference). This would probably fix your issue but it would need a null check before each use of the context.
Also, if you are using the Android LiveCycle ViewModel, you should know that you could use the AndroidViewModel (instead of simple ViewModel) that will require a context instance to be passed in the constructor, and you can use that instead of the Application context.

Saving a static Context variable in Application class

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.

AndroidViewModel vs ViewModel

With the introduction of the Android Architecture Components library, several new classes were introduced, including AndroidViewModel and ViewModel. However, I'm having trouble figuring out the difference between these two classes. The documentation succinctly describes AndroidViewModel as follows:
Application context aware ViewModel
I appreciate the brevity, but what exactly does this imply? When should we choose to use AndroidViewModel over ViewModel and vice-versa?
AndroidViewModel provides Application context
If you need to use context inside your Viewmodel you should use AndroidViewModel (AVM), because it contains the application context. To retrieve the context call getApplication(), otherwise use the regular ViewModel (VM).
AndroidViewModel has application context.
We all know having static context instance is evil as it can cause memory leaks!! However, having static Application instance is not as bad as you might think because there is only one Application instance in the running application.
Therefore, using and having Application instance in a specific class is not a problem in general. But, if an Application instance references them, it is a problem because of the reference cycle problem.
See Also about Application Instance
AndroidViewModel Problematic for unit tests
AVM provides application context which is problematic for unit testing. Unit tests should not deal with any of the Android lifecycle, such as context.
Finally I got something a simpler explanation, a bit......
...The AndroidViewModel class is a subclass of ViewModel and similar to them, they are designed to store and manage UI-related data are responsible to prepare & provide data for UI and automatically allow data to survive configuration change.
The only difference with AndroidViewModel is it comes with the application context, which is helpful if you require context to get a system service or have a similar requirement. the bold text makes it clearer to sense it.
AndroidViewModel is subclass of ViewModel. The Difference between them is we can pass Application Context which can be used whenever Application Context is required for example to instantiate Database in Repository.
AndroidViewModel is a Application context aware ViewModel.
AndroidViewModel:
public class PriceViewModel extends AndroidViewModel {
private PriceRepository priceRepository;
public PriceViewModel(#NonNull Application application) {
super(application);
priceRepository= new PriceRepository(application);
allPrices = priceRepository.getAllPrices();
}
ViewModel:
public class PriceViewModel extends ViewModel {
public PriceViewModel() {
super();
}
You Should use AndroidViewModel only when you require Application
Context.
You should never store a reference of activity or a view that references a activity in the ViewModel.Because ViewModel is designed to outlive a activity and it will cause Memory Leak.
Apart from the difference that AndroidViewModel gives you an application context whereas ViewModel does not. The important thing that you must understand is that Google itself recommends using ViewModel and not AndroidViewModel.
So, don't use AndroidViewModel unless it is really necessary.
See this: GOOGLE DOC

Will it leak? FirebaseAnalytics.getInstance in custom singleton

I still completely don't understand memory leaks. I have created custom singleton with static method:
public static AnalyticsHelper getInstance(Context context) {
return analyticsHelper == null ?
analyticsHelper = new AnalyticsHelper(FirebaseAnalytics.getInstance(context)) :
analyticsHelper;
}
and I want to know whether memory (activity) leaks will occur if I instantiate it in Activity. I'm not sure, because other Firebase tools works as ContentProvider and doesn't need Context when instantiating them. Official docummentation won't help me.
The accepted answer isn't correct. Firebase Analytics (and all the other Firebase singletons that accept a Context) don't hold the same context that you passed to it. It will use Context.getApplicationContext() on that Object behind the scenes to get a true singleton Context to hold for the lifetime of the object. This is the way intelligent Android APIs will work that require a Context to hold indefinitely.
The only potential problem in the given code is the fact that it could possible create two instances of AnalyticsHelper when called in rapid succession from two different threads, but that is unlikely.

Using objects obtained through android Context after the context is invalid

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" >

Categories

Resources