Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I get Resources(R.string) in viewModel in Android (MVVM and databinding)

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?

like image 493
Shubham Avatar asked Dec 04 '17 07:12

Shubham


People also ask

What is AndroidViewModel?

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.


4 Answers

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)
    }
}
like image 71
Expert wanna be Avatar answered Oct 08 '22 04:10

Expert wanna be


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

like image 33
alisonthemonster Avatar answered Oct 08 '22 05:10

alisonthemonster


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)
    }
}
like image 10
Ananthakrishnan K R Avatar answered Oct 08 '22 04:10

Ananthakrishnan K R


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");
}

}

like image 9
Bozbi Avatar answered Oct 08 '22 04:10

Bozbi