代码之家  ›  专栏  ›  技术社区  ›  dglozano

新Android推荐架构中的BoundService+LiveData+ViewModel最佳实践

  •  13
  • dglozano  · 技术社区  · 7 年前

    我一直在努力思考在新的系统中安卓服务的位置 Android recommended Architecture . 我想出了许多可能的解决办法,但我无法决定哪一个是最好的办法。

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

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

    现在,我将简要地解释一下我的设想,但只是为了使全景更清晰,而不是因为我想知道这个特定场景的答案。

    • 我有一个绑定到myActivity的BluetoothService和它的一个片段(因为我希望服务与activity具有相同的生命周期,但我也希望直接从片段中与它交互)。
      • 有关蓝牙连接状态的信息。不需要持久化。
      • 来自蓝牙设备的数据(这是一个秤,在本例中是体重和身体成分)。需要坚持下去。

    AndroidService中的LiveData LiveData inside Android Service arch

    • 包含连接状态和权重的LiveData 蓝牙设备的测量值在蓝牙服务内部。
    • 碎片可以触发BluetoothService中的操作(例如丑闻)
    • 片段观察关于连接状态的LiveData 状态已连接)。
    • 片段观察新重量测量的实时数据。如果一个新的重量测量来自蓝牙设备,那么片段会告诉它自己的ViewModel来保存新数据。它是通过一个存储库类完成的。

    fragment和AndroidService之间的共享ViewModel Shared ViewModel arch

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

    服务视图模型 Service ViewMOdel arch

    • BluetoothService在自己的ViewModel中更新与蓝牙相关的LiveData。
    • 片段在它自己的ViewModel和BluetoothService ViewModel中观察LiveData。

    有些人可能认为它们应该被视为一个数据源(因为在我的例子中,它是通过蓝牙从电子秤上获取数据的),但我认为这不是一个好主意,因为我在上一段已经说过了,特别是 because of what it says here

    避免将应用程序的入口点指定为活动, 服务 和广播接收器作为数据源。相反,它们应该只与其他组件协调以检索 组件的寿命相当短,这取决于用户的交互

    最后,我的问题是:

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

    3 回复  |  直到 7 年前
        1
  •  33
  •   Jeel Vankhede    5 年前

    更新时间:

    在得到易卜拉欣·迪苏基的建议后 我深入挖掘,发现了一些有趣的东西!这是背景。

    O、 P。 考虑到Android架构组件,Android框架的服务组件在哪里

    它与 Activity/Fragment . 怎样?如果您要扩展服务类而不是这样,那么就开始扩展 LifecycleService

    LifecycleService 现在它拥有自己的生命周期注册/维护器 ServiceLifecycleDispatcher LifecycleOwner .

    从现在开始,你可以 ViewModel 如果您遵循正确的应用程序体系结构并拥有存储库模式,那么为自己做操作会让您只能从单一的事实来源中找到答案!

    视图模型 为了完成和存储库层相关的业务逻辑,以及稍后的另一个生命周期感知组件,活动/片段也可以使用/重用相同的组件 视图模型

    生命周期所有者 (活动和生命周期服务) &其他生命周期感知组件(amp;O)。如果你没有准备好从旧的回叫方式的数据片段等回叫。


    (我建议不要通读)

    应与处于同一级别 活性/碎片 ,因为它是框架组件,而不是 MVVM公司 . 但是因为这个服务没有实现 LifecycleOwner 数据源 因为它可以作为应用程序的入口点。

    所以,这里的困境是有时 (就你而言) 服务作为数据源,向用户界面提供来自某个长期运行任务的数据。

    所以它应该在什么地方 Android架构组件 LifecycleObserver . 因为,无论您在后台做什么,您都需要考虑 生命周期所有者 .

    为什么?因为,我们通常会把它绑定到 (活动/片段) &要执行长时间关闭UI的任务(amp;N)。所以,它可以被当作 生命周期观察者 " !


    如何实施?

    1. 服务等级 实施 生命周期观察者

    2. 当你把你的服务绑定到 活性/碎片 ,在服务类的服务连接期间,通过调用方法将服务作为LifecycleObserver添加到活动中 getLifecycle().addObserver(service class obj)

    3. create resume

    在这种情况下,我们不会要求 LiveData 从服务升级到 视图模型 .


    WorkManager . (只是胡思乱想)

        2
  •  1
  •   Grant Park    7 年前

    避免直接接触Android服务的一种方法是通过接口对象。这是 “隔离”界面 在缩写中, 固体

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

    有了上述范例,您可以自由地将服务放在包含POJO代码的包之外的包中。没有“正确”的位置来放置您的服务——但是肯定有错误的地方放置它们(例如,您的POJO代码放在哪里)。

        3
  •  0
  •   brucemax    6 年前

        class OneBreathModeTimerService : Service() {
            var longestHoldTime = MutableLiveData<Int>().apply { value = 0 }
            ...
        }
    

         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) {}
         }
    

    我不是LiveData的专业人士,但是这种方法会有什么问题呢?

        4
  •  0
  •   uberchilly    6 年前

    如果我们像在onStart/onStop中一样从活动或多个活动绑定/取消绑定到服务,那么我们有一个单例实例,它保存着与蓝牙相关的管理器(我为blemanager使用了nordic lib)。该实例处于服务中,因此我们可以断开连接,例如,当服务被破坏时,因为ui与它解除了绑定,并在创建服务时重新连接到该绑定。我们还将blemanager singleton注入viewmodel中,以便通过livedata或rx或ble manager提供的类似反应性数据(例如连接状态)来简化交互和数据侦听。通过这种方式,我们可以从viewmodel与ble交互,订阅特征等,并且服务提供的作用域可以在多个活动中生存,并且基本上知道何时连接或断开连接。我已经在我的应用程序中尝试过这种方法,到目前为止效果还不错。

    示例项目 https://github.com/uberchilly/BoundServiceMVVM

        5
  •  -2
  •   M-Wajeeh Steve Bergamini    7 年前

    这样对待你的服务怎么样?

    enter image description here