新 Android 推荐架构中的最佳实践

我一直在努力思考在新的 Android 推荐的架构中安装 Android 服务的位置。我想出了许多可能的解决方案,但我不能决定哪一个是最好的方法。

我做了很多研究,但找不到任何有用的指南或教程。我找到的关于在我的应用程序架构中放置服务的唯一提示是这个,来自@JoseAlcerreca 中柱

理想情况下,ViewModel 应该对 Android 一无所知。这提高了可测试性、泄漏安全性和模块性。一般的经验法则是确保没有机器人。* 导入到 ViewModel 中(除了 android.arch 之外)。*).同样的道理也适用于演讲者。

根据这一点,我应该将我的 Android 服务放在架构组件层次结构的顶部,与我的活动和片段处于同一级别。这是因为 Android 服务是 Android 框架的一部分,所以 ViewModel 不应该知道它们。

现在,我将简要地解释我的情景,但只是为了使全景更清晰,不是因为我想要这个具体情景的答案。

  • 我有一个 Android 应用程序,它有一个包含许多片段的 MainActivity,所有片段都绑定在 BottomNavBar 中。
  • 我有一个绑定到 myActivity 和它的一个片段的 BluetoothService (因为我希望服务具有与活动相同的生命周期,但是我也希望直接从我的片段与它交互)。
  • 片段与 BluetoothService 交互以获得两种类型的信息:
    • 有关蓝牙连接状态的信息。不需要持久化。
    • 来自蓝牙设备的数据(这是一个刻度,所以在这种情况下体重和身体组成)。需要坚持。

下面是我能想到的3种不同的架构:

安卓服务中的 LiveData

更新: 这是我个人当时采用的方法,因为它工作得很好,让我能够相对快速地完成它。然而,我建议遵循 Jeel Vankhede 的更新答案,这似乎是一个更“惯用”的实现。

LiveData inside Android Service arch

  • 带有连接状态和权重的 LiveData 来自蓝牙设备的测量数据在蓝牙服务中。
  • 片段可以触发 BluetoothService 中的操作(例如 ScanDevice)
  • 片段观察 LiveData 关于连接状态的信息 并相应地调整 UI (例如,如果 状态连接)。
  • 片段观察新的重量测量的 LiveData。如果一个新的重量测量来自于 BluetoothDevice,那么这个片段就会告诉它自己的 ViewModel 来保存新的数据。它是通过 Repository 类完成的。

片段与 Android 服务之间的共享视图模型 Shared ViewModel arch

  • 片段可以触发 BluetoothService 中的操作(例如 ScanDevice)
  • BluetoothService 在共享 ViewModel 中更新与蓝牙相关的 LiveData。
  • 片段在它自己的 ViewModel 中观察 LiveData。

服务视图模型 Service ViewMOdel arch

  • 片段可以触发 BluetoothService 中的操作(例如 ScanDevice)
  • BluetoothService 在其自己的 ViewModel 中更新与蓝牙相关的 LiveData。
  • 片段在它自己的 ViewModel 和 BluetoothServiceViewModel 中观察 LiveData。

我很确定我应该把它们放在架构的顶部,就像对待活动/片段一样对待它们,因为边界服务是 Android 框架的一部分,它们由 Android 操作系统管理,并且它们与其他活动和片段绑定在一起。在这种情况下,我不知道什么是与 LiveData、视图模型和活动/片段交互的最佳方式。

有些人可能认为它们应该被看作是数据源(因为在我的例子中,它是通过蓝牙从一个比例尺中获取数据) ,但我不认为这是一个好主意,因为我在前一段所说的,特别是 因为上面写着:

避免指定应用程序的入口点,比如活动, 服务 和广播接收器ー作为数据源 与该入口点相关的数据子集。每个应用程序 组件是相当短命的,这取决于用户的交互 他们的设备和整个系统当前的健康状况。

最后,我的问题是:

我们应该把 Android (绑定)服务放在哪里? 它们与其他体系结构组件有什么关系?这些替代方案中有哪些是好的方法?

18374 次浏览

How about treating your service like this?

enter image description here

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)

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

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?

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

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