When working with ViewModels the View observes the ViewModel. It has to register as an observer. In the official tutorial of Google this registration is delegated to the observe() method of a LiveData object.
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
The method getUsers() returns the LiveData object itself. It's observe() method is used to register the observer. The View does not observe the the ViewModel but a part of it's implementation.
Now is this best practice, when working with ViewModels not to observe themselves but parts of their implementation in form of LiveData objects? Or is this an introduction of low quality?
Based on the answer of Chris I give my own answer. I think the tutorial is not best practice for the simple reason, that an object should not expose it's internal implementation. Base on the argumentation of Chris I was looking for an option to get encapsulation without losing the named features. The result is the method observerUsers() which delegates to a LiveData object internally.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivityViewModel model = ViewModelProviders.of(this).get(MainActivityViewModel.class);
model.observeUsers(this,
new Observer<List<User>>() {
#Override
public void onChanged(#Nullable List<User> users) {
updateUI();
}
}
);
}
void updateUI() {
}
static class MainActivityViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public void observeUsers(#NonNull LifecycleOwner owner,
#NonNull Observer<List<User>> observer) {
getUsers().observe(owner, observer);
}
private LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
static class User {
}
}
Still List<User> exposes internal implementation. It could be improved to a class Users.
I put everything into one file and use inner static class. This is not meant as best practice. It was just to be able to quickly edit everything within one file. Especially the model User belongs into it's very own file, while I often put the ViewModel into the View class it belongs to like this.
My second point of critic matches the case that the ViewModel itself observes an underlying model. In this case the observer method onChange() is very general and requires a very general update method like updateUI(). You may want to observe more specific events of the model to do specific updates.
I'd say yes it's best practice for the ViewModel to expose its data through some form of Observable, whether that be LiveData or something like an RX Observable.
This breaks from other architectures such as MVP where the presenter would typically have a reference to the View which gets called when something changes. The guidelines are quite specific about what a ViewModel should reference.
A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
By exposing your data as an Observable through the ViewModel this means views can come and go, and once subscribed will receive the latest data and any subsequent updates. Again the guidelines have some detail.
If the activity is re-created, it receives the same MyViewModel instance that was created by the first activity. When the owner activity is finished, the framework calls the ViewModel objects's onCleared() method so that it can clean up resources
https://developer.android.com/topic/libraries/architecture/viewmodel.html
I am using ViewModel, introduced in IO/17.
I am using following guidelines provided on android developers page.
https://developer.android.com/topic/libraries/architecture/viewmodel.html
Following is their sample code.
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// do async operation to fetch users
}
}
I wish to perform Volley request in the 'loadUsers()' method. But I cannot do it as it needs a 'context' as follows
Volley.newRequestQueue(context).add(jsonObjectRequest);
So my question is,
Is it recommended(or possible) to perform network operations inside a ViewModel??
If yes(if possible), how to do it?
You could use the AndroidViewModel class instead of ViewModel. AndroidViewModel holds a reference to the application context.
https://youtu.be/5qlIPTDE274
Consider Dagger, that way you don't have to worry, about providing context for Volley from your ViewModel.
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.You must use AndroidViewModel for Application Context.
public class MyViewModel extends AndroidViewModel {
private MutableLiveData<List<User>> users;
private Application application;
public MyViewModel(#NonNull Application application) {
this.application=application;
super(application);
}
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Pass Context to Repository
}}
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.
Is it recommended(or possible) to perform network operations inside a
ViewModel??
No, you should not perform Networking Operations inside a ViewModel.
If yes(if possible), how to do it?
Get Application Context by using AndroidModelView and pass it to Repository,
as recommended by Android Team.
I'm using Dagger to create activity specific object graphs. Within this subgraph, I make use of a Singleton MyPresentationModel.
When i exit my activity, and enter the activity again, my expectation is that a new instance of the activity specific object graph is created, which in turn would create a new instance of Singleton MyPresentationModel (by virtue of the #Singleton semantic per Dagger. See this So answer for specifics) which would then last for the life of the activity specific object graph.
However, this is not what i'm observing, every time the activity specific object graph is created, the same instance of MyPresentationModel is used. I added a debug point into the constructor of MyPresentationModel. The very first time we enter the constructor. Subsequently even on activity exits and reentries, we don't enter the constructor (and because of this the UserSession being used within my Presentation model uses the old value from the very first constructor injection).
While i can technically solve the problem by re-setting UserSession inside MyPresentaitonModel with an external public setter, I want to understand better the mechanics of the activity specific object graph creation/destruction.
By nullifying the graph in my onDestroy, does that still mean that there is a possibility of the Singletons within my subgraph being reused at a later point ? (possibly until they are truly GCed?)
Here's some code:
// MyAppModule
#Module(
includes = { UserSession.class},
injects = { MyApplication.class })
public class MyAppModule {
private final MyApplication _app;
MyAppModule(MyApplication app) {
_app = app;
}
// ...
}
// Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activityObjectGraph = MyApplication.get()
.getObjectGraph()
.plus(Arrays.<Object>asList(new SubModule()).toArray());
// Inject ourselves so subclasses will have dependencies fulfilled when this method returns.
_activityObjectGraph.inject(this);
}
#Override
protected void onDestroy() {
_activityObjectGraph = null;
// this eagerly allows GC, but doesn't necessarily destroy the subgraph ?
super.onDestroy();
}
// SubModule
#Module(injects = { MyPresentationModel.class, MainActivity.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule {}
}
// MyPresentationModel
#Singleton
public class MyPresentationModel {
private UserSession _session;
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
public void someMethodThatUsesSessionInfo() {
// _session.getUser() ...
}
}
#weefbellington posted a very informative answer, but reading it made me realize my question was not specific and clear enough. Here's attempt 2:
MyAppModule (main graph) -> provides a Singleton UserSession
MySubModule (sub graph plused onto MyAppModule) -> provides "activity specific" Singleton MyPresentationModel which requires a UserSession (provided my MyAppModule) on construction.
I now close the activity, destroying MySubModule (and also hopefully MyPresentationModel which is a Singleton), I update UserSession with some new information.
I open MainActivity again, thus re-creating the sub-graph from MySubModule, which inturn provides a MyPresentationModel.
The issue I'm noticing is that MyPresentationModel which is the local Singleton is not being reconstructed again i.e. this part of the code:
#Inject
public MyPresentationModel(UserSession session) {
_session = session;
}
is only ever being called once. My expectation was that this part of the code would be run again, and the UserSession would be pulled again from the Main graph and since it was updated, it would hold the updated values. My question is: are Singletons within the sub-graph cached in anyway or will they always be recreated when a new activity sub-graph is spawned?
How MyPresentationModule is injected depends on how your modules are specified. For example, assume that you are injecting the class Foo:
public class Foo {
private final MyPresentationModel model;
#Inject
public Foo(MyPresentationModel model) {
this.model = model;
}
}
If your modules are structured like (A), then the MyPresentationModel singleton will be injected into Foo by the main object graph:
EXAMPLE A
#Module(injects = { Foo.class })
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { MyPresentationModel.class })
public class SubModule { ...}
Alternatively, if your modules are structured like (B), then the MyPresentationModel singleton will be injected into Foo by the subgraph:
EXAMPLE B
#Module
public class MainModule { ... }
#Module(addsTo = MainModule.class, injects = { Foo.class, MyPresentationModel.class })
public class SubModule { ... }
In your particular case, since you have specified that MyAppModule injects MyApplication, I would guess that you are trying to inject MyPresentationModel into your Application class. This is probably not what you want to do. You probably want inject this into your Activity class using the submodule, as in (C).
EXAMPLE C
#Module(injects = { MainActivity.class, MyPresentationModel.class },
addsTo = MyAppModule.class,
library = true)
public class SubModule { ... }
public class MainActivity {
#Inject MyPresentationModel presentationModel;
...
}
If you do this the MyPresentationModel singleton will be bound to the Activity subgraph instead of the main graph, and should be disposed when the Activity is destroyed.
Once you have a handle on Dagger, you might want to check out Mortar, which gives you finer-grained control over creation and destruction of ObjectGraph subscopes.
Alright, I'm having an issue trying to mix frameworks.
So, I have a #SharedPref annotated class that should generate a Shared Preferences manager from Android Annotations. The class looks a bit something like this:
DownloadPrefs.java
#SharedPref(value= SharedPref.Scope.UNIQUE)
public interface DownloadPrefs {
#DefaultBoolean(false)
boolean hasEnabledDownload();
#DefaultBoolean(false)
boolean showedDownloadDialog();
#DefaultLong(0)
long downloadRefreshedOn();
}
Now, I'd like to inject the resulting class (which will be DownloadPrefs_) into a Fragment to make use of it. The fragment has had working injection before adding the new module, so I'm only going to write here what I added:
Fragment.java
#Inject DownloadPrefs_ downloadPrefs;
Now, since the actual DownloadPrefs_ class is generated at runtime, it would make the most sense to create an #Provides annotation for it, since I can't mark a constructor as injected. Nor does the DownloadPrefs_ have a no-arg constructor. The module I'm using then receives the new #Provides:
DownloaderModule.java
#Provides //#Singleton // Does not work with/out #Singleton
DownloadPrefs_ provideDownloadPrefs() {
return new DownloadPrefs_(MinimalBible.getApplication());
}
To be technical about it, the DownloadPrefs_ constructor that gets generated by Android Annotations expects a Context passed to it, I would have guessed that the Application context would be suitable. Otherwise, I'm not sure how I could possibly get access to the Activity context. Or whether that would actually break the ObjectGraph.
However, when I go to run the actual injection, I get the following message:
Caused by: java.lang.IllegalStateException: Errors creating object graph:
org.bspeice.minimalbible.activities.downloader.DownloadPrefs_ has no injectable members. Do you want to add an injectable constructor? required by class org.bspeice.minimalbible.activities.downloader.BookListFragment
Any clue on what's going on? It doesn't seem like the questions asking about "no injectable members" on other SO questions answered my case. I had a working app before adding the code above.
UPDATE: After doing some double-checking, I came across the following weird behavior. If I copy out the pre-built Android Annotations class, rename it, and inject that, everything works. Additionally, I can verify that the original built Android Annotations class (the DownloadPrefs_.java) does in fact exist in the .dex, so Dagger should have no reason to not be able to find it. Everything is doing a debug build, so I can't imagine ProGuard is messing anything up.
At this point, I'm going to create a minimal project to demonstrate the error, and file an issue with Dagger. In the mean time, just need to rewrite the Prefs class until I can get this sorted out.
UPDATE 5/12/2014
Here are the modules responsible for injection:
MinimalBibleModules.java
#Module(
injects = {
MinimalBible.class
},
includes = {
ActivityModules.class
}
)
public class MinimalBibleModules {
}
ActivityModules.java
#Module(
includes = {
ActivityDownloaderModule.class
}
)
public class ActivityModules {
}
ActivityDownloaderModule.java
#Module(
injects = {
BookListFragment.class,
DownloadManager.class,
BookRefreshTask.class
}
)
public class ActivityDownloaderModule {
#Provides #Singleton
DownloadManager provideDownloadManager() {
return new DownloadManager();
}
#Provides
EventBus provideBus() {
return new EventBus();
}
#Provides //#Singleton
DownloadPrefs_ provideDownloadPrefs() {
return new DownloadPrefs_(MinimalBible.getApplication());
}
}
Also, how the graph gets created:
MinimalBible.java
public class MinimalBible extends Application {
private ObjectGraph graph;
private static MinimalBible instance;
public MinimalBible() {
instance = this;
}
#Override
public void onCreate() {
graph = ObjectGraph.create(new MinimalBibleModules());
graph.inject(this);
}
There are two parts here. First, how you get access to the Context. You can do static things as you are, though that's not advisable. Generally, you should configure your graph with a stateful module that carries the context, like this:
#Module
class ApplicationModule {
private final Application application;
public ApplicationModule(Application app) {
this.application = app;
}
// you can mark this singleton, but it's minor overhead
// and the fact that you have a single instance stored
// means it's semantically equivalent. But for clarity
// it's sometimes good to make the point.
#Provides
#Singleton
Application application() {
return application;
}
// optionally: bind it as a Context with a qualifier.
// note: never bind Context without a qualifier annotation
// as Activity and Application are both Context subtypes.
#Provides
#Singleton
#PerApplication
Context appContext(Application app) {
// Doing this instead of returning this.application is
// semantically equivalent but links #PerApplication Context
// to Application, so in graph analysis and error reporting
// the link is clearer. That's a personal choice.
return app;
}
}
At any rate, you then when you create the graph:
Application appInstance = ...;
ObjectGraph appGraph = ObjectGraph.create(
MyAppModule.class,
new ApplicationModule(appInstance));
The Application is then seeded into the graph and can be depended-upon by other types that declare it as a dependency.
Is there a way to get the current Context instance inside a static method?
I'm looking for that way because I hate saving the 'Context' instance each time it changes.
Do this:
In the Android Manifest file, declare the following.
<application android:name="com.xyz.MyApplication">
</application>
Then write the class:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Now everywhere call MyApplication.getAppContext() to get your application context statically.
The majority of apps that want a convenient method to get the application context create their own class which extends android.app.Application.
GUIDE
You can accomplish this by first creating a class in your project like the following:
import android.app.Application;
import android.content.Context;
public class App extends Application {
private static Application sApplication;
public static Application getApplication() {
return sApplication;
}
public static Context getContext() {
return getApplication().getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
sApplication = this;
}
}
Then, in your AndroidManifest you should specify the name of your class in the AndroidManifest.xml’s tag:
<application
...
android:name="com.example.App" >
...
</application>
You can then retrieve the application context in any static method using the following:
public static void someMethod() {
Context context = App.getContext();
}
WARNING
Before adding something like the above to your project you should consider what the documentation says:
There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), the function to retrieve it can be given a
Context which internally uses Context.getApplicationContext() when
first constructing the singleton.
REFLECTION
There is also another way to get the application context using reflection. Reflection is often looked down upon in Android and I personally think this should not be used in production.
To retrieve the application context we must invoke a method on a hidden class (ActivityThread) which has been available since API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
There is one more hidden class (AppGlobals) which provides a way to get the application context in a static way. It gets the context using ActivityThread so there really is no difference between the following method and the one posted above:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Happy coding!
Assuming we're talking about getting the Application Context, I implemented it as suggested by #Rohit Ghatol extending Application. What happened then, it's that there's no guarantee that the context retrieved in such a way will always be non-null. At the time you need it, it's usually because you want to initialize an helper, or get a resource, that you cannot delay in time; handling the null case will not help you.
So I understood I was basically fighting against the Android architecture, as stated in the docs
Note: There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), include Context.getApplicationContext() as a Context argument when invoking your singleton's getInstance() method.
and explained by Dianne Hackborn
The only reason Application exists as something you can derive from is because during the pre-1.0 development one of our application developers was continually bugging me about needing to have a top-level application object they can derive from so they could have a more "normal" to them application model, and I eventually gave in.
I will forever regret giving in on that one. :)
She is also suggesting the solution to this problem:
If what you want is some global state that can be shared across different parts of your app, use a singleton. [...] And this leads more naturally to how you should be managing these things -- initializing them on demand.
so what I did was getting rid of extending Application, and pass the context directly to the singleton helper's getInstance(), while saving a reference to the application context in the private constructor:
private static MyHelper instance;
private final Context mContext;
private MyHelper(#NonNull Context context) {
mContext = context.getApplicationContext();
}
public static MyHelper getInstance(#NonNull Context context) {
synchronized(MyHelper.class) {
if (instance == null) {
instance = new MyHelper(context);
}
return instance;
}
}
the caller will then pass a local context to the helper:
Helper.getInstance(myCtx).doSomething();
So, to answer this question properly: there are ways to access the Application Context statically, but they all should be discouraged, and you should prefer passing a local context to the singleton's getInstance().
For anyone interested, you can read a more detailed version at fwd blog
No, I don't think there is. Unfortunately, you're stuck calling getApplicationContext() from Activity or one of the other subclasses of Context. Also, this question is somewhat related.
Here is an undocumented way to get an Application (which is a Context) from anywhere in the UI thread. It relies on the hidden static method ActivityThread.currentApplication(). It should work at least on Android 4.x.
try {
final Class<?> activityThreadClass =
Class.forName("android.app.ActivityThread");
final Method method = activityThreadClass.getMethod("currentApplication");
return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
// handle exception
} catch (final NoSuchMethodException e) {
// handle exception
} catch (final IllegalArgumentException e) {
// handle exception
} catch (final IllegalAccessException e) {
// handle exception
} catch (final InvocationTargetException e) {
// handle exception
}
Note that it is possible for this method to return null, e.g. when you call the method outside of the UI thread, or the application is not bound to the thread.
It is still better to use #RohitGhatol's solution if you can change the Application code.
Kotlin way:
Manifest:
<application android:name="MyApplication">
</application>
MyApplication.kt
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication
private set
}
}
You can then access the property via MyApplication.instance
It depends on what you are using the context for. I can think of at least one disadvantage to that method:
If you are trying to create an AlertDialog with AlertDialog.Builder, the Application context won't work. I believe you need the context for the current Activity...
Kotlin
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
and get Context like
MyApp.mInstance
or
MyApp.getContext()
If you're open to using RoboGuice, you can have the context injected into any class you want. Here's a small sample of how to do it with RoboGuice 2.0 (beta 4 at time of this writing)
import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;
import javax.inject.Inject;
#ContextSingleton
public class DataManager {
#Inject
public DataManager(Context context) {
Properties properties = new Properties();
properties.load(context.getResources().getAssets().open("data.properties"));
} catch (IOException e) {
}
}
}
I've used this at some point:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
This is a valid context I used at getting system services and worked.
But, I used it only in framework/base modifications and did not try it in Android applications.
A warning that you must know: When registering for broadcast receivers with this context, it will not work and you will get:
java.lang.SecurityException: Given caller package android is not running in process ProcessRecord
If you don't want to modify the manifest file, you can manually store the context in a static variable in your initial activity:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
And just set the context when your activity (or activities) start:
// MainActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Note: Like all other answers, this is a potential memory leak.
in Kotlin, putting Context/App Context in companion object still produce warning Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
or if you use something like this:
companion object {
lateinit var instance: MyApp
}
It's simply fooling the lint to not discover the memory leak, the App instance still can produce memory leak, since Application class and its descendant is a Context.
Alternatively, you can use functional interface or Functional properties to help you get your app context.
Simply create an object class:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
or you could use it more safely using nullable type:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
and in your App class add this line:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
and in your manifest declare the app name to . MyApp
<application
android:name=".MyApp"
When you wanna get the context simply call:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Hope it will help.
You can use the following:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Any other class:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
According to this source you can obtain your own Context by extending ContextWrapper
public class SomeClass extends ContextWrapper {
public SomeClass(Context base) {
super(base);
}
public void someMethod() {
// notice how I can use "this" for Context
// this works because this class has it's own Context just like an Activity or Service
startActivity(this, SomeRealActivity.class);
//would require context too
File cacheDir = getCacheDir();
}
}
JavaDoc for ContextWrapper
Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.
I think you need a body for the getAppContext() method:
public static Context getAppContext()
return MyApplication.context;
If you for some reason want Application context in any class, not just those extending application/activity, maybe for some factory or helper classes. You can add the following singleton to your app.
public class GlobalAppContextSingleton {
private static GlobalAppContextSingleton mInstance;
private Context context;
public static GlobalAppContextSingleton getInstance() {
if (mInstance == null) mInstance = getSync();
return mInstance;
}
private static synchronized GlobalAppContextSingleton getSync() {
if (mInstance == null) mInstance =
new GlobalAppContextSingleton();
return mInstance;
}
public void initialize(Context context) {
this.context = context;
}
public Context getApplicationContext() {
return context;
}
}
then initialize it in your application class's onCreate with
GlobalAppContextSingleton.getInstance().initialize(this);
use it anywhere by calling
GlobalAppContextSingleton.getInstance().getApplicationContext()
I don't recommend this approach to anything but application context however. As it can cause memory leaks.
I use a variation of the Singleton design pattern to help me with this.
import android.app.Activity;
import android.content.Context;
public class ApplicationContextSingleton {
private static Activity gContext;
public static void setContext( Activity activity) {
gContext = activity;
}
public static Activity getActivity() {
return gContext;
}
public static Context getContext() {
return gContext;
}
}
I then call ApplicationContextSingleton.setContext( this ); in my activity.onCreate() and ApplicationContextSingleton.setContext( null ); in onDestroy();
I just released a jQuery-inspired framework for Android called Vapor API that aims to make app development simpler.
The central $ facade class maintains a WeakReference (link to awesome Java blog post about this by Ethan Nicholas) to the current Activity context which you can retrieve by calling:
$.act()
A WeakReference maintains a reference without preventing the garbage collection reclaiming the original object, so you shouldn't have a problem with memory leaks.
The downside of course is that you run the risk that $.act() could return null. I have not come across this scenario yet though, so it's perhaps just a minimal risk, worth mentioning.
You can also set the context manually if you are not using VaporActivity as your Activity class:
$.act(Activity);
Also, much of the Vapor API framework uses this stored context inherently which might mean you needn't store it yourself at all if you decide to use the framework. Check out the site for more information and samples.
I hope that helps :)
Rohit's answer seems correct. However, be aware that AndroidStudio's "Instant Run" depends on not having static Context attributes in your code, as far as I know.
Today the right way to have context is to use dependency injection.
For instance, one can use Hilt to inject context at any place it is needed. Let's say one needs context in some database manager, then this can be resolved in the following way:
Add Hilt in Gradle:
implementation "com.google.dagger:hilt-android:2.35"
kapt "com.google.dagger:hilt-android-compiler:2.35"
Define Application class with #HiltAndroidApp annotation (let it inject the database manager for example):
#HiltAndroidApp
class MyApplication : Application() {
#Inject
lateinit var dbManager: DBManager
override fun onCreate() {
super.onCreate()
dbManager.initDB()
}
}
Define Database manager (let it be #Singleton for example as well):
#Singleton
class DBManager #Inject constructor(
#ApplicationContext private val context: Context
) {
fun initDB() {
// context is avaiable
databaseInit(context)
}
}
And that's it. The DBManager can access context in the right way without memory leaks.
Another alternative to get context without subclassing the Application object and without using hidden classes would be to use a ContentProvider. Once the onCreate method is called, the context should be available. You can do something like this in Kotlin
class ContextContentProvider : ContentProvider() {
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = 0
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun onCreate(): Boolean {
applicationContext = context
return true
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? = null
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
) = 0
companion object {
private var applicationContext: Context? = null
#JvmStatic
fun applicationContext() = applicationContext
}
}
Anywhere you need the context, you can call ContextContentProvider.applicationContext() method
Make sure to use a different authority in the AndroidManifest.xml if you already have another content provider and the content provider is not exported.
<application>
<provider
android:name=".ContextContentProvider"
android:authorities="${applicationId}.ContextContentProvider"
android:enabled="true"
android:exported="false" />
</application>