Каков правильный способ наблюдения LiveData? - PullRequest
0 голосов
/ 15 апреля 2020

Предположим, что в ViewModel есть объект LiveData, содержимое которого представляет интерес для нескольких представлений. Каждое представление обрабатывается по-разному и может отображаться или не отображаться в любой момент времени, независимо от других представлений.

Какой из этих подходов лучше / правильнее?

  1. Чтобы иметь 1 наблюдателя для такого объекта LiveData и иметь наблюдателя для реализации обновления logi c для всех представлений, делая необходимые проверки, et c.

  2. Чтобы иметь несколько наблюдатели объекта LiveData (или каждого заинтересованного представления), и каждый наблюдатель имеет дело только с логами c этого конкретного представления.

Что я заметил до сих пор, так это следующий вариант 1 может привести к коду, который трудно управлять и понимать. Одно и то же представление может быть обновлено в разных частях кода, что снижает удобочитаемость. Вариант 2 решает эту проблему, позволяя размещать весь код, связанный с представлением. Однако меня беспокоит снижение производительности, связанное с наличием нескольких наблюдателей на одной и той же LiveData в одном и том же фрагменте.

Какой из них является наиболее подходящим для сложных представлений?

Пример:

class NewViewModel() : ViewModel() {
    private val _lv1 = MutableLiveData<Boolean>(false)
    val lv1: LiveData<Boolean> = _lv1

    private val _lv2 = MutableLiveData<Boolean>(false)
    val lv2: LiveData<Boolean> = _lv2
}

class NewFragment : Fragment() {

    private var _viewBinding: FragmentNewBinding? = null
    private val viewBinding
        get() = _viewBinding!!

    private val viewModel by viewModels<NewViewModel> { getViewModelFactory() }

    override fun onCreateView(...): View? {
        _viewBinding = FragmentNewBinding.inflate(inflater)

        return viewBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        example1()

        example2_1()
        example2_2()
    }

    override fun onDestroyView() {
        _viewBinding = null

        super.onDestroyView()
    }

   private fun example1() {
        val view1 = viewBinding.view1
        viewModel.lv1.observe(viewLifecycleOwner, Observer {
            // Some complex-ish UI logic that might need
            // to retrieve supplement data from somewhere (e.g Glide)
            view1.refreshWith(it)
        })

        val view2 = viewBinding.view2
        viewModel.lv1.observe(viewLifecycleOwner, Observer {
            // Some complex-ish UI logic that might need
            // to retrieve supplement data from somewhere (e.g Glide)
            view2.refreshWith(it)
        })
    }

    private fun example2_1() {
        val view1 = viewBinding.view1

        viewModel.lv2.observe(viewLifecycleOwner, Observer {
            // Some complex-ish UI logic that might need
            // to retrieve supplement data from somewhere (e.g Glide)
            view1.refreshWith(it)
        })
    }

    private fun example2_2() {
        val view2 = viewBinding.view2

        viewModel.lv2.observe(viewLifecycleOwner, Observer {
            // Some complex-ish UI logic that might need
            // to retrieve supplement data from somewhere (e.g Glide)
            view2.refreshWith(it)
        })
    }
}

Ответы [ 2 ]

0 голосов
/ 15 апреля 2020

На мой взгляд, первый подход гораздо лучше. потому что

1) У нас один источник данных, один наблюдатель, все данные передаются по каналу через одного, поэтому не будет никакой вероятности человеческой ошибки, как в случае нескольких наблюдателей, люди могут писать похожая логика в более чем одном наблюдателе.

2) Как вы упомянули сами, накладных расходов на дополнительных обсерсеров не будет.

3) Преимущество нескольких наблюдателей, которые вы сказали, предположим, что вы наблюдаете данные ответа API, вы снова нажмете API, вы получите все данные, вам придется передать данные обратно и придется заполнить данные для всех представлений, поэтому даже данные для некоторых представлений не изменились Тем не менее, они будут заполнены, поэтому перед заполнением либо вы должны сравнить новые поступающие данные с ранее загруженными данными, так что вы можете сделать это и в случае одного наблюдателя.

Я могу ошибаться в своих наблюдениях, поправьте меня, если я ошибаюсь.

0 голосов
/ 15 апреля 2020

1) Если для нескольких представлений требуются разные части данных LiveData, чтобы использовать Transformations для сопоставления одной LiveData с несколькими Sub LiveDatas. например, мне нужно имя пользователя от пользователя в одном представлении и возраст пользователя от другого представления.

Преобразование LiveData

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

2) Но если ваши представления основаны на в другом состоянии, например, УСПЕХ - СБОЙ - ЗАГРУЗКА Используйте Single LiveData с классом данных MyResponse.

data class MyResponse<T>(
    var status: Status,
    var data: T? = null,
    var throwable: Throwable? = null
) {

    companion object {

        fun <T> loading(data: T?=null): MyResponse<T> {
            return MyResponse(
                status = Status.LOADING,
                data = data
            )
        }

        fun <T> success(data: T): MyResponse<T>? {
            return MyResponse(
                status = Status.SUCCESS,
                data = data
            )
        }

        fun <T> failed(throwable: Throwable): MyResponse<T> {
            return MyResponse(
                status = Status.FAILED,
                throwable = throwable
            )
        }
    }
}

enum class Status {
    LOADING,
    SUCCESS,
    FAILED,
}

И в вашей ViewModel

val mSettingHeaderLiveData = MutableLiveData<MyResponse<UserInfo>>()

Пример использования

mSettingMainViewModel.mSettingHeaderLiveData.observe(this, Observer { userInfoResponse ->
            if (userInfoResponse.status == Status.SUCCESS) {
                // Do something on View_1
            } else if (userInfoResponse.status == Status.Failed) {
               // Do something on View_2
            } else if (userInfoResponse.status == Status.Loading) {
               // Do something on View_3
            } 
        })
...