undefined
undefined

Reputation: 695

MutableLiveData for collections

I request data from server by bunches and store it in the array.To track fetching of the next bunch of the data I have this class.In the addItems method I notify diffObservers and pass list of new items:

class PackItems:MutableLiveData<ArrayList<GetPacksResponse.PackData>>() {
        private var diffObservers=ArrayList<Observer<List<GetPacksResponse.PackData>>>()
        private var active=false

        fun observeItems(owner: LifecycleOwner, valueObserver:Observer<List<GetPacksResponse.PackData>>,diffObserver:Observer<List<GetPacksResponse.PackData>>) {
            super.observe(owner,valueObserver)
            diffObservers.add(diffObserver)
        }

        override fun removeObservers(owner: LifecycleOwner) {
            super.removeObservers(owner)
            diffObservers= ArrayList()
        }

        fun addItems(toAdd:List<GetPacksResponse.PackData>) {
            value?.addAll(toAdd)
            if (active)
                for (observer in diffObservers)
                    observer.onChanged(toAdd)
        }

        override fun onActive() {
            super.onActive()
            active=true
        }

        override fun onInactive() {
            super.onInactive()
            active=false
        }
    }

The problem is PackItems is MutableLiveData and it's not good practice to expose it.Is there way to cast it to LiveData?Like usually we do:

private val _items = MutableLiveData<List<Int>>()
val items: LiveData<List<Int>> = _items


UPD:Ideally would be if I could expose completely immutable LiveData.But I can't just write

private val _packs:PackItems=PackItems()
val packs:LiveData<ArrayList<GetPacksResponse.PackData>>
get()=_packs

Because in this case packs won't contain observeItems method.Therefore there must be custom class derived from LiveData like:

open class PackItems: LiveData<ArrayList<GetPacksResponse.PackData>>() {
    protected var active=false
    protected var diffObservers = ArrayList<Observer<List<GetPacksResponse.PackData>>>()

    fun observeItems(owner: LifecycleOwner, valueObserver: Observer<List<GetPacksResponse.PackData>>, diffObserver: Observer<List<GetPacksResponse.PackData>>) {
        super.observe(owner,valueObserver)
        diffObservers.add(diffObserver)
    }
   //...
}

class MutablePackItems: PackItems() {
    fun addItems(toAdd:List<GetPacksResponse.PackData>) {
        value?.addAll(toAdd)
        if (active)
            for (observer in diffObservers)
                observer.onChanged(toAdd)
    }
}

But in this case I won't be able to set data because now MutablePackItems is LiveData(immutable) :)

Upvotes: 0

Views: 487

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170723

I'd consider using composition instead of inheritance:

class PackItems() {
    private val mutableData = MutableLiveData<ArrayList<GetPacksResponse.PackData>>()
    val asLiveData: LiveData<ArrayList<GetPacksResponse.PackData>> get() = mutableData
    ...

    fun observeItems(owner: LifecycleOwner, valueObserver:Observer<List<GetPacksResponse.PackData>>,diffObserver:Observer<List<GetPacksResponse.PackData>>) {
        mutableData.observe(owner,valueObserver)
        diffObservers.add(diffObserver)
    }

    fun removeObservers(owner: LifecycleOwner) {
        mutableData.removeObservers(owner)
        diffObservers = ArrayList()
    }

    // etc
}

EDIT: to set active as in your original code, may be a bit nastier:

    private val mutableData = object : MutableLiveData<ArrayList<GetPacksResponse.PackData>>() {
        override fun onActive() {
            super.onActive()
            active = true
        }

        override fun onInactive() {
            super.onInactive()
            active = false
        }
    }

EDIT 2:

but the main problem is I need to return custom LiveData class with custom observeItems method

The point is that you don't necessarily. Whenever you'd call LiveData's method (e.g. observe), just call items.asLiveData.observe(...) instead. If you want to pass it to another method foo accepting LiveData, call foo(items.asLiveData).

In principle, you could modify this approach by extending LiveData and delegating all calls to mutableData:

class PackItems(): LiveData<ArrayList<GetPacksResponse.PackData>>() {
    private val mutableData = MutableLiveData<ArrayList<GetPacksResponse.PackData>>()
    ...

    fun observeItems(owner: LifecycleOwner, valueObserver:Observer<List<GetPacksResponse.PackData>>,diffObserver:Observer<List<GetPacksResponse.PackData>>) {
        mutableData.observe(owner,valueObserver)
        diffObservers.add(diffObserver)
    }

    override fun observe(owner: LifecycleOwner, observer: ArrayList<GetPacksResponse.PackData>) {
        mutableData.observe(owner, observer)
    }

    override fun removeObservers(owner: LifecycleOwner) {
        mutableData.removeObservers(owner) // not super!
        diffObservers = ArrayList()
    }

    // etc
}

but I don't think it's a good idea.

Upvotes: 2

Related Questions