Android LiveData и шаблон провайдера / менеджера - PullRequest
0 голосов
/ 15 сентября 2018

Я работаю над приложением, в котором источник данных определен в Provider / Manager. Этот класс (назовем его InfoProvider) в значительной степени является просто черным ящиком - он имеет некоторые свойства и вызывает их при выполнении, что приводит к изменению этих свойств (аналогично тому, как работает репозиторий, но вместо вызовов, возвращающих значения, они выполняют асинхронный вызов, который приведет к изменению одного или нескольких свойств провайдера).

Эта настройка предназначена специально для Bluetooth Low Energy - мы все знаем, насколько плохо он работает на Android, и я хотел сделать его как можно более асинхронным и использовать привязку данных + aliveata + viewmodels для достижения полностью адаптивной архитектуры.

С Xamarin это было бы легко, просто определите InfoProvider как поле в ViewModel и привяжите его к полям. Однако я не обязательно хочу показывать все поля во всех моделях представления (некоторые могут нуждаться только в состоянии батареи устройства, некоторые могут нуждаться в полном доступе, некоторые могут просто выполнять функции, не ожидая ответа). Для функций это легко прокси, но для LiveData<T> я не нашел много информации. Как мне пойти дальше и «обойти» поле LiveData?

Пример:

class InfoProvider {
    var batteryPercent = MutableLiveData<Int>()

    public fun requestBatteryUpdate() {
        [...]
        batteryPercent.value = newValue
    }
}

// ViewModel for accessing device battery, inheriting from architecture ViewModel
class DeviceBatteryViewModel: ViewModel() {
    var batteryPercentage = MutableLiveData<Int>()
    val infoProvider: InfoProvider by inject()

    init {
        // TODO: Subscribe this.batteryPercentage to infoProvider.batteryPercent

    fun onButtonClick() {
        infoProvider.requestBatteryUpdate()
    }
}

class DeviceBatteryFragment: Fragment() {
    val ViewModel: DeviceBatteryViewModel by inject()
    private lateinit var binding: DeviceBatteryBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
            DeviceBatteryBinding.inflate(inflater, container, false).also { binding = it }.root

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.viewModel = this.ViewModel
    }

}


// res/layout/fragment_devicebattery.xml

<layout [namespaces]>
    <data class="bindings.DeviceBatteryBinding>
        <variable android:name="viewModel" type=".DeviceBatteryViewModel />
    </data>

    <WhatEverLayout [...]>
        <TextView [...] android:text="@{viewModel.batteryPercentage.toString()}" />
        <Button [...] android:onClick="@{() -> viewModel.onButtonClick()}" />
    </WhatEverLayout>
</layout>

Чего я хотел бы избежать, так это обмена в стиле Rx .observe(() -> {}), .subscribe(() -> {}) и т. Д. Можно ли это сделать (т. Е. Если я назначу значение infoProvider.batteryPercent для поля batteryPercentage виртуальной машины, будет ли оно также получать обновления), или я должен привязаться непосредственно к инфо-провайдеру?

1 Ответ

0 голосов
/ 18 января 2019

Нет пути к "pass around" the LiveData field без звонка batteryPercent.observe(...).Кроме того, вам нужно будет использовать Lifecycler Owner для наблюдения за полем (если вы не хотите ObserveForever, что не является рекомендуемым решением).

Мое предложение будет выглядеть примерно так:

InfoProvider {
    val repositoryBatteryUpdate = BehaviorSubject.create<Int>()

    fun observeRepositoryBatteryUpdate(): Observable<Int> {
        return repositoryBatteryUpdate
    }

    fun requestBatteryUpdate(){
        // Pseudo code for actually update

        // Result goes into repositoryBatteryUpdate.onNext(...)
    }
}

ViewModel{
    val status: MutableLiveData<Int>

    init{
        repository.observeRepositoryItems()
            .subscribe( update -> status.postValue(status))
    }

    fun update(){
        repository.requestBatteryUpdate()
    }
}

Fragment{
    viewModel.status.observe() // <-- Here you observe Updates

    viewModel.update()
}

Обратите внимание, что вам придется избавиться от подписки в ViewModel onCleared.
Обратите внимание, что все это псевдокод, и его следует делать намного чище, чем этот.

...