MVVM shared prefs in a remote class - java

So I'm implementing my first Android project with the MVVM pattern by following the official Android docs schema (below) and other internet resources.
In another activity I used a PreferenceFragmentCompat to save some user settings. Now I need those settings in a Remote data source class, but without the context I can't access the Shared Prefs.
Any suggestion on how to accomplish that?
P.S. I'm using Java...

From their documentation (https://developer.android.com/reference/androidx/preference/PreferenceFragmentCompat)
To retrieve an instance of SharedPreferences that the preference hierarchy in this fragment will use by default, call PreferenceManager.getDefaultSharedPreferences(android.content.Context) with a context in the same package as this fragment.
I don't believe this is referring to the exact package the file is in, but the overall top level package of your source files. As I am able break out my fragments/activities in different packages from the one where my PreferenceFragmentCompat is located in and able to retrieve the stored settings.
I wouldn't advise passing in the fragment/activity context to your Remote Data Source class, but if you have dagger setup you could inject an instance of PreferenceManager class created from app context through the constructor and fetch whatever settings you need. Or, you can pass those settings that your data source requires from your view -> vm -> repository/data source.
Using the MVVM with repository pattern (your data source class) I would do something like the following:
1.) Create a ViewModel which holds an instance of your data source/repository class.
2.) From your view (activity/fragment), obtain an instance of PreferenceManager and fetch the settings you need.
3.) Pass them along to your viewModel which then passes it along to your data source/repository.
4.) Do whatever you need in your data source class with those settings
... inside your view class
val pref = PreferenceManager.getDefaultSharedPreferences(context).getString(PREF_KEY_NAME, null)
viewModel.yourMethod(pref)
... inside your viewmodel class
fun yourMethod(pref: String?) {
repository.doSomething(pref)
}
... inside your repository/data source class
fun doSomething(pref: String?) {
// whatever you need to do with this pref.
// e.g. api call
api.doMethod(pref)
}

You can have an interface PreferenceStorage and inject it into your RemoteDataSource. You can test your remote data source with a mock preference storage.
Here is the example from Google I\O app:
https://github.com/google/iosched/blob/master/shared/src/main/java/com/google/samples/apps/iosched/shared/data/prefs/PreferenceStorage.kt
In Kotlin
class RemoteDataSource #Inject constructor(private val preferenceStorage: PreferenceStorage) {
...
}

Related

how do i remove uri path information?

I have requirement like i need to deleted selected file information if anybody selected wrong file, but after call delete() method its deleted from gallery also,
my question is how can remove selected file URI information i dont want to delete selected file form gallery, any help?
Working with Android Architecture Components, such as: View Model, LiveData, etc. I would like to suggest you to follow Official Guide from Android.
When using Android Architecture Components to call a API request, how I try to achieve my goal is as follow:
Create one Repo class, in that class, initialize the Retrofit Interface(if using Retrofit). Create a function that calls the required API, handle request response and returns a Live Data. Make extra functions for the parameter to be passed with URL.
Create one View Model class, in that class, initialize above Repo class. Create a function that calls repo class function which returns Live Data and this function also returns LiveData. Make extra functions for the parameter to be passed to Repo for adding with URL.
In your View class (Activity/Fragment), initialize View Model class and pass those parameters which are required URL params. Call the View Model function, which returns Live Data and observe that in your View class.
You will get data in your View Class when any change appears in Live Data.
Above is a simple practice, but it can be made good by making responses generic according to your requirement.
You can create public function in VM and pass desired paramas to it and then invoke desired URL from repo .
Refer example
https://github.com/googlesamples/android-architecture-components/blob/master/BasicSample/app/src/main/java/com/example/android/persistence/viewmodel/ProductListViewModel.java
you can call some public method from viewmodel and then pass params to it.somehow like this
viewmodel
class UserViewmodel: ViewModel() {
fun callApi(userId: String) : Any {
// Your method definition
return result
}
}
and in activity / fragment call method via viewmodel instance
class UserActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
val viewModel = ViewModelProviders.of(this).get(UserViewmodel::class.java)
val result = viewModel.callApi("userId")
}
}

What is the point of injecting a ViewModelFactory in Android - Dagger 2

I have recently started with Android Development, and coming from an iOS background, concepts like Dependency Injection frameworks and ViewModelFactories are a new thing for me. In all the tutorials I saw, ViewModels always extended the ViewModel class provided by android.arch.lifecycle.ViewModel. If the viewModel had parameters then a ViewModelFactory had to be created and injected in an activity by Dagger
#Provides
#ActivityScope
fun provideViewModelFactory(dependency: Dependency) : ViewModelProvider.Factory = CustomViewModelFactory(dependency)
CustomViewModelFactory will then be in charge of creating ViewModels. This all works fine, however, I'm not really understanding the point of a viewModelFactory when I can just inject the viewModels directly into an activity sort of like this:
#Module
class ViewModelModule(private val user: User) {
#ActivityScope
#Provides
fun provideMainViewModel() = MainViewModel(user = user)
fun provideOtherViewModel() = OtherViewModel()
}
#ActivityScope
#Subcomponent(modules = [ViewModelModule::class])
interface MainActivitySubComponent {
fun inject(activity: MainActivity)
}
#ApplicationScope
#Component()
interface ApplicationComponent {
fun addMainActivitySubComponent(viewModelModule: ViewModelModule) : MainActivitySubComponent
}
And in my Activity
class MainActivity : AppCompatActivity() {
#Inject lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val subComponent = (application as MainApplication).component.addMainActivitySubComponent(ViewModelModule(User("NEW NAME")))
subComponent.inject(this)
}
}
So what is the point of a ViewModelFactory when I can simply inject a viewModel to in my activity right away?
You use the ViewModelProviders and the ViewModelProviders.Factory to ensure that you get the same instance of ViewModel across configuration changes. So in the scope of an Activity, the ViewModel created by the ViewModelProviders is only created once.
It's also required for expected behavior of the onCleared() callback that ViewModels have.
In Kotlin, I prefer to use this linked approach compared to the multi-binding one. Although it does require you to know about the "injector" who can create the view model.
Let's take a look at what happens when you inject your ViewModel:
Your Activity gets (re)created.
You create a (Sub)Component for said Activity.
You inject the dependencies.
The interesting part is 3, where we will inject some scoped objects (e.g. Singletons) but create new objects for the rest of our dependencies.
Every time you (re)create your Activity (-> configuration change) those objects will be created again, and you'll lose state. You can use savedInstanceState to preserve some data, or you could figure out some other means to save state (e.g. Singletons, retained Fragments, ...)
ViewModel on the other hand promises to keep state between orientation changes. When we request a ViewModel from ViewModelProviders after an orientation change we will receive the same object as before. We don't need to worry about saving and restoring the state.
We would recreate the factory, but Android/ the Support Library/Jetpack/Arch Components (whatever it's called now) will store the Viewmodel and only create it when it hasn't been created before. The previous model will be reused during configuration changes.
So if you want to inject the ViewModel directly you can obviously do so, but your ViewModel won't keep its state between orientation changes.

Overriding ViewModelStore

Is it possible to provide once own implementation of a ViewModelStore for ViewModelProviders to use instead of the default one?
More precisely, I'm interested in adding fun clear(vm: ViewModel) (or using an index or something similar) functionality to the ViewModelStore so that I can clear a single view model of my choice, not just use the built in ViewModelStore#clear:
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
which clears all view models.
First, I think you should not consider doing that, because that's an implementation detail of Architecture Components library. Most possibly you should come up with a better solution as a result of adapting your use-case to match guidelines/contracts exposed by ViewModels API.
Nevertheless, let's examine possibilities of doing that.
Here's the code, that we should use in order to obtain a ViewModel implementation:
val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
What will this code result in, is that it will create an instance of HolderFragment, which is a retained fragment, and will attach it to this's fragment manager (might be either FragmentActivity's fragment manager or Fragment's child fragment manager).
This HolderFragment will be added with a HolderFragment.HOLDER_TAG, thus we are able to get an instance of this fragment from the fragment manager.
val holderFragment = supportFragmentManager.findFragmentByTag("android.arch.lifecycle.state.StateProviderHolderFragment") as HolderFragment
It's the HolderFragment, that creates an instance of ViewModelStore and keeps that instance as a private field. There exists a getter for that field, but there does not exist a setter, which means, that the only way to "substitute" this object is by using reflection.
But before doing that, let's try to write a custom implementation of ViewModelStore class:
class MyViewModelStore : ViewModelStore() {
private val mMap = HashMap<String, ViewModel>()
internal fun put(key: String, viewModel: ViewModel) {
val oldViewModel = mMap.put(key, viewModel)
oldViewModel?.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
}
internal operator fun get(key: String): ViewModel? {
return mMap[key]
}
override fun clear() {
for (vm in mMap.values) {
vm.onCleared() // COMPILATION ERROR -> Cannot access 'onCleared': it is protected/*protected and package*/ in 'ViewModel'
}
mMap.clear()
}
}
Unfortunately, we cannot do that, because ViewModel#onCleared() has a protected package access, which makes impossible for us call it outside of the android.arch.lifecycle package. Again, we can use reflection to do that (but how good is that?).
Despite being not advised (by me), seems like that's also not achievable to do (without using reflection).

Dynamically set the authority of a ContentProvider

Perhaps the title is a bit misleading. My problem is that I have an Android library project which is shared between two standard Android projects: one for a free version of the app and the other for a paid version. The library currently has the code for a ContentProvider, including a contract class with several static String variables for things such as the URI and column names. Now I want the "authority" for the URI to change depending on which app is using the library. One solution that comes to mind is storing the authority as a string resource and loading that string at run-time into the static final String variable. However, I'm not sure how to do this as the contract class has a private constructor and no Context object in order to load the string resource. What other options are available to solve my problem?
Here's a better solution for those using newer versions of the build tools: make the authority relative to your application ID. You can do this automatically using ${applicationId}, which is expanded into your app's application ID during the build process.
<provider
android:name=".MyContentProvider"
android:authorities="${applicationId}.provider"/>
Let's say your application IDs are com.example.app.paid and com.example.app.free. When you build your app, the authority will become com.example.app.paid.provider and com.example.app.free.provider, correspondingly.
To reference the provider authority in your code, use BuildConfig.APPLICATION_ID + ".provider".
Using different authorities for the free and the paid version makes sense in case the user tries to install both versions.
I'm defining a different authority for the two versions in the manifest like so:
<provider
android:name="MyApp.MyProvider"
android:authorities="MyApp.MyProvider.free"
android:grantUriPermissions="true"/>
Then I configure the provider in an xml file (I use a special config.xml file because I have more configuration data like the provider authority, but you can use strings.xml of course):
<string name="my_provider_authority">MyApp.MyProvider.free</string>
The code retrieves the provider authority as any other string resource. To access string resources without a context use the application context. I'm using an application class to have access to the application context from anywhere in my app (there are two exceptions though):
public class MyApplication extends Application {
private static Context sContext;
#Override
public void onCreate() {
super.onCreate();
sContext = this;
}
public static Context getContext() {
return sContext;
}
}
Of course you need to define MyApplication in your manifest.
This allows you to access string and other resources from anywhere in your app.
There are two exception though:
ContentProviders. ContentProviders can be started before Application starts and so you won't have an Application context available. That's no problem though because ContentProviders get their own context through getContext().
Static code: the context might not be available outside the life cycle of Android components (Activities, Fragments, BroadcastReceivers, Services etc.). Static initializers that are relying on the application context are therefore not a good idea. But that's also not a real issue because using a context outside the life cycle of Android components isn't allowed anyway and static methods accessing a context would always be called from within that life cycle. E.g. if an Activity needs to know a ContentProvider's authority it would call a static method in your contract class and that call would be from one of the activity's onXYZ() methods like onCreate() or onStart() which would make sure that the context is initialized. So all you need to do is lazy initialize the variables in your contract class and make sure the caller does retrieve the variables only when it's clear that Application.onCreate() has been called before. Of course from within an activity you could retrieve the string resources directly. The real advantage of my method will become obvious when you need the resources in other classes/objects. These objects would still be tied to the life cycle of some Android component but you wouldn't have to pass around the context to all these objects, which is 1) very cumbersome and 2) very error prone when it comes to leaking the context which could lead to memory usage issues (one of the most common problems with Android apps).
Why change the authority at all? You're not required to export the provider, which means that nobody could even see the authority name except by deconstructing the app. Even then, they wouldn't be able to access the provider.
If it's for your own internal convenience, then I'd use the same authority but put different security on the URIs.
In short, your idea is interesting, but I wouldn't do it that way. Too much of a mess.

Register in android a session like value shared by all activity

How can I register data (like integer or poco objects) shared by all activity like the id of the user ? Have I to use a simple singleton or is there a special Android way ?
Note : I don't need to make that data persistant (no need of SharedPreferences or sqlite)
Thank you
You can create your own class that implements Application and specify this in your manifest file. In that case, every time you call getApplicationContext you will get a reference of your application that can hold any kind of information.
How to declare global variables in Android?
Sample code:
class MyApplication extends Application {
public void setMethod() {
//
}
}
((MyApplication)getApplicationContext()).setMethod()
The android way is to create a custom Application for your project. Then in onCreate of that application you initialize whatever you need, and for example from an Activity do something like:
((MyApplication) getApplication()).getMyData()
If using roboguice you can use a #Singleton injection which basically does the boilerplate of a singleton for you - that's much nicer.

Categories