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

后端 未结 5 1381
半阙折子戏
半阙折子戏 2020-12-12 15:06

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

相关标签:
5条回答
  • 2020-12-12 15:33

    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?

    1. Take your service class and implement LifecycleObserver interface to it.

    2. 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)

    3. 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)

    0 讨论(0)
  • 2020-12-12 15:33

    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).

    0 讨论(0)
  • 2020-12-12 15:35

    How about treating your service like this?

    0 讨论(0)
  • 2020-12-12 15:38

    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?

    0 讨论(0)
  • 2020-12-12 15:44

    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

    0 讨论(0)
提交回复
热议问题