I am currently using databinding
and MVVM architecture
for android. What would be the best way to get string resources in ViewModel.
I am not using the new AndroidViewModel
component, eventbus
or RxJava
I was going through the aproach of interfaces where Activity will be responsible for providing resources. But recently I found a similar question with this answer where a single class using application context is providing all resources.
Which would be the better approach? or is there something else that I can try?
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.
You can access the context by implementing AndroidViewModel instead of ViewModel.
class MainViewModel(application: Application) : AndroidViewModel(application) {
fun getSomeString(): String? {
return getApplication<Application>().resources.getString(R.string.some_string)
}
}
You can also use the Resource Id and ObservableInt to make this work.
ViewModel:
val contentString = ObservableInt()
contentString.set(R.string.YOUR_STRING)
And then your view can get the text like this:
android:text="@{viewModel.contentString}"
This way you can keep the context out of your ViewModel
an updated version of Bozbi's answer using Hilt
ViewModel.kt
@HiltViewModel
class MyViewModel @Inject constructor(
private val resourcesProvider: ResourcesProvider
) : ViewModel() {
...
fun foo() {
val helloWorld: String = resourcesProvider.getString(R.string.hello_world)
}
...
}
ResourcesProvider.kt
@Singleton
class ResourcesProvider @Inject constructor(
@ApplicationContext private val context: Context
) {
fun getString(@StringRes stringResId: Int): String {
return context.getString(stringResId)
}
}
Just create a ResourceProvider class that fetch resources using Application context. In your ViewModelFactory instantiate the resource provider using App context. You're Viewmodel is Context free and can be easily testable by mocking the ResourceProvider.
Application
public class App extends Application {
private static Application sApplication;
@Override
public void onCreate() {
super.onCreate();
sApplication = this;
}
public static Application getApplication() {
return sApplication;
}
ResourcesProvider
public class ResourcesProvider {
private Context mContext;
public ResourcesProvider(Context context){
mContext = context;
}
public String getString(){
return mContext.getString(R.string.some_string);
}
ViewModel
public class MyViewModel extends ViewModel {
private ResourcesProvider mResourcesProvider;
public MyViewModel(ResourcesProvider resourcesProvider){
mResourcesProvider = resourcesProvider;
}
public String doSomething (){
return mResourcesProvider.getString();
}
ViewModelFactory
public class ViewModelFactory implements ViewModelProvider.Factory {
private static ViewModelFactory sFactory;
private ViewModelFactory() {
}
public static ViewModelFactory getInstance() {
if (sFactory == null) {
synchronized (ViewModelFactory.class) {
if (sFactory == null) {
sFactory = new ViewModelFactory();
}
}
}
return sFactory;
}
@SuppressWarnings("unchecked")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(MainActivityViewModel.class)) {
return (T) new MainActivityViewModel(
new ResourcesProvider(App.getApplication())
);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With