0

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

undefined
  • 623
  • 7
  • 27

1 Answers1

2

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.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • composition is a fresh idea but the main problem is I need to return custom `LiveData` class with custom `observeItems` method – undefined Sep 13 '19 at 12:18
  • Alexey, I'd like to do in fragment: `model.items.asLiveData.observeItems(...)`.Your approach won't allow this as there is no `observeItems` method in `LiveData` – undefined Sep 13 '19 at 12:50
  • And for `observeItems` in particular you just do `model.items.observeItems(...)`, no `asLiveData`. – Alexey Romanov Sep 13 '19 at 12:57
  • oh you're referring `not a good idea` section.Ok I got the idea.Let me try it – undefined Sep 13 '19 at 13:07
  • nope.How do I update `mutableData` in this case? If there will be public api to do it then I'd better to give to a fragment `mutableData` itself – undefined Sep 13 '19 at 13:14
  • If you want to allow arbitrary updates, yes. I assume not, so you have methods like `addItems`... – Alexey Romanov Sep 13 '19 at 13:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/199437/discussion-between-undefined-and-alexey-romanov). – undefined Sep 13 '19 at 14:18