How can I access my activity's ViewModel from within a service? - android

I am building an app that needs to detect certain objects in photos. I first load and keep all the photos I'd like to inspect into my room Database and connect it to my view with a ViewHolder.
When the user clicks a button, I want the detection process to begin, and I want to use a jobIntentService for that (As there might be thousands of photos there).
My problem is - how do I access the view holder from within the Service? I need it both to actually get a hold of the files and also so I can update each file's record once detection has been made.
I've tried to ask for the activity as one of the attributes, but I am getting this error
Unable to instantiate service tech.levanter.anyvision.services.DetectJobIntentService: java.lang.InstantiationException: java.lang.Class<tech.levanter.anyvision.services.DetectJobIntentService> has no zero argument constructor
Would appreciate any input, thanks

ViewModels holds particular significance for Activities and Fragments (e.g. they retain data during config changes). So a Service doesn't really need it. Hence you can resolve the issue in one of two ways.
Approach 1:
If your MyViewModel is just a wrapper for accessing LiveData from your Repository class, then you can just use your Repository class inside your Service.
Approach 2:
If your ViewModel is doing more than just wrapping calls to the Repository and you want your Service class to have access to the same logic defined in your ViewModel, then use an intermediate ViewModelContent class. Instead of putting everything in your MyViewModel class, put them in a "ViewModelContent" class. Then use your MyViewModel class as an accessor wrapper around ViewModelContent. Then your Service can instantiate ViewModelContent as you would any other class.
class MyViewModel(application: Application) : AndroidViewModel(application) {
init{
viewModelContent = ViewModelContent(...)
}
}
Approach 1 will usually be cleaner than Approach 2.

Related

User/Auth object in Android - where does it belong: ViewModel, MainActivity, extended Application object, or someplace else

I am struggling on the guidance around how we manage objects with global scope when using fragments. I have a user/auth object and need it in almost every fragment as it is required for calling webapis. Normally I try to use ViewModels but that seems like a 1 per fragment (or shared by multiple fragments as in master/detail) is the recommendation. So the idea that a AuthViewModel is instantiated in each fragment does not seem right. I could keep a user object in the fragment and use it for authentication or use it in an extended Application object.
Create a AuthViewModel with a authobject and use it in each fragment
Store the authobject in the MainActivity and use it in each fragment as required
Extend the Application object so it can be used by each fragment
Or is there another option?
I need the object in case a call fails so that I can refresh the access token or even the user if their token has expired.
If it is simply Auth related object, I usually prefer to keep it below the business layer, ViewModel and Fragments/Activities should have no reason to deal with low-level objects. Preferably this object should exist on the network layer or even better an Auth Layer that sits on top of the network layer, which can be a singleton and be shared among those that need it.

Is it okay to access activity's properties and functions from fragment?

I'm new to android, I wanted to know if it is okay to access properties initialized in activity / call activity functions from fragment like this or is it bad practice and I should avoid it.
(requireContext() as BaseActivity).viewModel
(requireContext() as BaseActivity).countryList
(requireContext() as BaseActivity).getSomething()
Your instincts can be right. Breaking changes can be caused by name conflicts, variable shadowing, wrong imports, wrong assignment to values. But these days, the demand for features is increasing, in such that you need the public accessor. Just have this rules in your conscience:
Interfaces are powerful at class to class communication
Inherit what is important, override what is implemented already, pass to param to lessen global var damage
If a variable can be stored in another global form, consider it with regard to size(ram matters), speed of access(ux matters), security(keys matter) and volatility(nulls matter).
Now looking at your code, I can see you have a fragment system that is based on values/functions stored in the main activity, that provides the context for the fragment. If you apply the first point: Your fragment will implement a BaseFragment that already some context cast i.e. lateinit var mainActivity: MainActivity then you can mainActivity.viewModel anywhere in your fragment without casting. And this is cleaner
Applying the second point: in the BaseFragment (that will be inherited by AnotherFragment)
abstract var viewModel: ViewModel
abstract fun initList()
open var countryList = mutableListOf()
open fun onScale(detector: ScaleGestureDetector) { //pinch: increase visible country list like some nice zoom effect .. etc }
if most or all of your fragments need similar functions or variables, make abstract to something you can forget will crash the app, make open for those 'features but I dont need to rewrite so I'll call super.function' functions. Make a var open if some super function overrides it, and just put var if you seriously dont know when you want it and when to change it.
On the third point, Android in the early stages, we learnt the hard way that context doesn't last forever even if your app is running. Rotation and lifecycle functions will swap it rough and fast. So consider other storage ways. I still dont trust requireContext/Activity/view for context, so cast with caution.
A big NO. It is a bad practice to use hardcode references to activity from fragments.
I see that you are using viewmodel, which indicates that you are using MVVM, you should use Sharing data between Activities and Fragments in MVVM made simple with SharedViewModel concept for communicating to viewmodel of the activity.
For communicating to the Activity which hosts your Fragment you should use an interface pattern of communication. from fragment to the activity
Let's say one of your activity won't extend the base activity as it has to extend one activity from a library lets say YouTubeBaseActivity, and it will host a particular fragment now the cast to BaseActivity will never succeed in your activity.

Activity scoped regular class that acts as a ViewModel

In the Using Dagger in your Android app codelab tutorial they use an activity scoped regular class that acts as a ViewModel like so
#ActivityScope
class RegistrationViewModel #Inject constructor(val userManager: UserManager) {
...
}
That makes ViewModel injection by Dagger very simple but won't we loose anyting if we don't derive from the architecture components ViewModel class?
In general, the code labs are related to some topic and they try to explain only this topic. Here it is Dagger, not Architecture Components. Yes, you can lose some functionality, but if they still can make their point - it does not matter.
Also if they make the app work with only plain java objects it means that they don't need the extra functionality from the ViewModel, they wrote less code so even better.
I also want to point out that the explanation that "you are losing ViewModel.onCleared" is "the smallest problem". What is the "main feature" of the VM is that you can share the same instance through the life cycle of the same Activity/Fragment or that you can share it between different Activity/Fragment.
And onCleared is something that should be used with caution because it means in some situations that you are trying to clear a reference to a thing you should not be holding in the first place.

BoundService + LiveData + ViewModel best practice in new Android recommended architecture

I been struggling a lot thinking about where to place Android Services in the new Android recommended Architecture. I came up with many possible solutions, but I cannot make up my mind about which one is the best approach.
I did a lot of research, and I couldn't find any useful guideline nor tutorial. The only hint I found about where to place the Service in my app architecture is this one, from #JoseAlcerreca Medium post
Ideally, ViewModels shouldn’t know anything about Android. This improves testability, leak safety and modularity. A general rule of thumb is to make sure there are no android.* imports in your ViewModels (with exceptions like android.arch.*). The same applies to presenters.
According to that, I should place my Android Services on the top of my Architecture Components hierarchy, at the same level as my Activities and Fragments. That's because Android Services are part of the Android framework, so ViewModels shouldn't know about them.
Now, I will explain briefly my scenario, but only to make the panorama clearer, not because I want an answer for this specific scenario.
I have an Android Application that has a MainActivity with many fragments in it, all of them tied together in a BottomNavBar.
I have a BluetoothService bound to myActivity and one of its fragments (because I want the Service to have the same lifecycle as the Activty but I also want to interact with it directly from my fragment).
The fragment interacts with the BluetoothService to get two types of information:
Information about the state of the Bluetooth connection. Doesn't need to be persisted.
Data that comes from the Bluetooth Device (it is a Scale, so weight and body composition in this case). Needs to be persisted.
Here are the 3 different architectures I can think of:
LiveData inside AndroidService
UPDATE: This is the approach I personally went with at the time because it worked well and allowed me to get it done relatively fast. However, I suggest following the updated answer by Jeel Vankhede for what seems to be a more "idiomatic" implementation.
The LiveData with the state of the connection and with the weight
measurements coming from the Bluetooth Device are inside the BluetoothService.
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The Fragment observes the LiveData about the state of the connection
and adapts the UI accordingly (for example, enable a button if the
state is connected).
The Fragment observes the LiveData of the new weight measurements. If a new weight measurement comes from the BluetoothDevice, the Fragment then tells its own ViewModel to save the new data. It is done via a Repository class.
Shared ViewModel between fragment and AndroidService
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The BluetoothService updates the Bluetooth related LiveData in the shared ViewModel.
The Fragment observes the LiveData in its own ViewModel.
Service ViewModel
The Fragment can trigger operations in the BluetoothService (scanDevices for example)
The BluetoothService updates the Bluetooth related LiveData in its own ViewModel.
The Fragment observes the LiveData in its own ViewModel and the BluetoothService ViewModel.
I am pretty sure I should place them on top of the architecture and treat them just like an Activity/Fragment, because BoundServices are part of the Android Framework, they are managed by the Android OS and they are bound to other Activities and Fragments. In that case, I don't know what's the best way to interact with LiveData, ViewModels and Activities/Fragments.
Some might think that they should be considered as a DataSource (since in my case it's getting data from a scale using Bluetooth), but I don't think this is a good idea, because of all what I've said in the previous paragraph and specially because of what it says here:
Avoid designating your app's entry points—such as activities,
services, and broadcast receivers—as sources of data. Instead, they should only coordinate with other components to retrieve the
subset of data that is relevant to that entry point. Each app
component is rather short-lived, depending on the user's interaction
with their device and the overall current health of the system.
So, finally, my question is:
Where should we place our Android (Bound) Services and what is their relation with the other architectural components? Is any of these alternatives a good approach?
Updated:
After getting suggestion from #Ibrahim Disouki (Thank you for that) I dig deeper and found out something interesting! Here's background.
O.P. seeks for solution "Where Service component of Android Framework stands considering Android Architecture Components". So, here's out the box(SDK) solution.
It stands at the same level as Activity/Fragment. How? If you're extending Service class though rather than that, start extending LifecycleService. Reason behind that is simple that previously we had to rely on Activity/Fragment lifecycle in order to receive updates/do some contextual operations on Service. But now it's not the case.
LifecycleService now has it's own lifecycle registry/maintainer called ServiceLifecycleDispatcher which takes care of lifecycle of service which also makes it LifecycleOwner.
It leaves us in condition that from now on, You can have a ViewModel to LifecycleService doing operations for itself & if you're following proper app architecture and having repository pattern leaves you to single source of truth!
In context of O.P. LifecycleService now can have ability to maintain it's ViewModel to do business logic related to repository layer, and later on another lifecycle aware component like, Activity/Fragment can also consume/reuse same ViewModel to have their specific operations to it.
Please note that by doing so, you're in state of having two different LifecycleOwners (Activity & LifecycleServie) which means you can't share view models between LifecycleService & other lifecycle aware components. If you don't like approach then be good with old callback kind of approach having callbacks back to Activity/Fragment from service when data is ready to serve etc.
Obselete:
(I suggest not to read through)
In my opinion, Service should be on same level as Activity/Fragment, because it's Framework component & not MVVM. but because of that Service doesn't implements LifecycleOwner and it's Android Framework Component, it shouldn't be treated as data source because it can be entry point to application.
So, dilemma here is that sometimes (In your case), Service acts as data source which provides data from some long running task to UI.
So what it should be in Android Architecture Component? I think you can treat it as LifecycleObserver. because, no matter what you do in background, you'll need to consider about lifecycle of the LifecycleOwner.
Why? because, we usually do bind it to LifecycleOwner (Activity/Fragments) & to do long running tasks off the UI. So, it can be treated like LifecycleObserver. In such a way we made our Service as "Lifecycle aware component" !
How you can implement it?
Take your service class and implement LifecycleObserver interface to it.
When you bind your service to Activity/Fragment, during your service connection of your service class, add your service to your activity as LifecycleObserver by calling method getLifecycle().addObserver(service class obj)
Now, Take an interface in service class to provide callback from service to your UI and every time your data changes, check that if your service has at least on Lifecycle event create or resume to provide callback with.
In such a way, we won't require LiveData to update to from service and even no ViewModel (Why do we need it for service? We don't need configuration changes to survive on service lifecycle. And main task to VM is that to consist data between lifecycles).
Side Note: If you think you're having long running background operations, then consider using WorkManager. After using this library, you'll feel like Services should be marked as deprecated by now! (Just a random thought)
One way to avoid direct contact with an Android service while still being able to use it is through an interface object. This is part of the "I" for Interface Segregation in the acronym, SOLID. Here is a small example:
public interface MyFriendlyInterface {
public boolean cleanMethodToAchieveBusinessFunctionality();
public boolean anotherCleanMethod();
}
public class MyInterfaceObject implements MyFriendlyInterface {
public boolean cleanMethodToAchieveBusinessFunctionality() {
BluetoothObject obj = android.Bluetooth.nastySubroutine();
android.Bluetooth.nastySubroutineTwo(obj);
}
public boolean anotherCleanMethod() {
android.Bluetooth.anotherMethodYourPresentersAndViewModelsShouldntSee();
}
}
public class MyViewModel {
private MyFriendlyInterface _myInterfaceObject;
public MyViewModel() {
_myInterfaceObject = new MyInterfaceObject();
_myInterfaceObject.cleanMethodToAchieveBusinessFunctionality();
}
}
Given the above paradigm, you are free to place your services in a package that's outside your packages that contain POJO code. There is no "right" location to put your services -- but there are definitely WRONG places to put them (e.g. where your POJO code goes).
What if we bind/unbind to/from service from activity or multiple activities as usual in onStart/onStop, then we have singleton instance that holds Bluetooth related manager (I use nordic lib for ble manager) . That instance is in service so that we can disconnect for example when service is destroyed because ui unbound from it and reconnect to ble when service is created. We also inject that ble manager singleton into viewmodel to make interaction and data listening easier via livedata or rx or similar reactive data provided by ble manager, for example for connection state. This way we can interact from viewmodel with ble, subscribe to characteristics etc and service is there to provide scope that can survive over multiple activities and basically knows when to connect or disconnect. I have tried this approach in my app and so far it works ok.
Sample project
https://github.com/uberchilly/BoundServiceMVVM
In my opinion using LiveData in servise is comfortable
class OneBreathModeTimerService : Service() {
var longestHoldTime = MutableLiveData<Int>().apply { value = 0 }
...
}
Then in fragment
override fun onCreate(savedInstanceState: Bundle?) {
mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
mOneBreathModeService = (binder as OneBreathModeTimerService.MyBinder).service
mOneBreathModeService!!.longestHoldTime.observe(this#OneBreathModeFragment, androidx.lifecycle.Observer {
binding.tvBestTime.text = "Best $it"
})
}
override fun onServiceDisconnected(name: ComponentName) {}
}
I am not pro in LiveData, but what can be wrong with such approach?
This question confuse me a long time. I don't think bind service should have a viewModle , as we known , the service are not view layer !
By the way, Service need to start/bind,unbind at activity
Finally, I think a easy and not bad way is the LiveData inside AndroidService. But not use Livedata to send data, use custom callback method.
Live data only send the newest data every time, if you need got the complete data also the page is onPause, there is a mistake.( Now, we can use kotlin flow)
Also We not just receive data from ble(bluetooth low energe) device, we also need send data to ble device.
There is a simple project code:
https://github.com/ALuoBo/TestTemp/blob/main/bluetooth/src/main/java/com/lifwear/bluetooth/BLECommService.java
How about treating your service like this?

how to use viewmodel singleton for activities?

MyApp need hold a User object in whole context,A,B,C activities'xml use this User object,when A edit User,I want B and C notifyChange,how to deal this problem with databinding,livedata and viewModel?
Formerly I make User.class extend BaseObservable,but POJO will be very troublesome and must not be a null,sometimes User maybe null such as not login.
Now I Change to use LiveData, make Pojo simple and not extend BaseObservable,but when A edit,B and C not work,I think i need ABC use same viewModel instance in memory,but this will cause viewModel's onClear() trigger manytimes.
Another way is to have one singleton repository to hold your user data and each viewModel can have that repository and share the same data between activities.
Based on this part of the documentation:
https://developer.android.com/topic/libraries/architecture/livedata#extend_livedata
The fact that LiveData objects are lifecycle-aware means that you can share them between multiple activities, fragments, and services. To keep the example simple, you can implement the LiveData class as a singleton as follows:
You can create a singleton for your view model like I did here:
companion object{
private lateinit var instance: ViewModelProfile
#MainThread
fun getInstance(userId: String): ViewModelProfile{
instance = if(::instance.isInitialized) instance else ViewModelProfile(userId)
return instance
}
}
Then I call it and get instance anywhere like this:
val profileVModel = ViewModelProfile.getInstance(user.uid)
If you want to share common ViewModel between ABC activities, then it is suggested to keep them as 3 fragments in a single Activity, create ViewModel of that Activity which can be shared among all three fragments A, B, and C.
Also what you are trying to achieve with activities is like this, suppose you have done some operation in activity A, if you want Activity B and C to get notified about them then they need to be running to get notified, which won't be happening, so instead you should use Intent or Bundle to pass needed information when the activity get started.
Updated
There are other ways as well to achieve similar kind of functionality like,
Event Bus
RxJava Subjects (refer this)
Android State by Evernote (refer this)
This will allow you to have application level access of state, which can be accessed by any Activity or Fragment

Categories

Resources