2

I'm writing a wrapper around FirebaseFirestore snapshot listener that emits the changes using RxKotlin Observable.

I wrote the following class which makes use of create() method to create the observable and emit the changes asynchronously when a new data snapshot is available.

The problem is that I'm leaking memory every time I create an instance of this class and I stop using it. What is the best way to rewrite this class without leaking memory?

Any resources about how to create Observables that can emit objects from listeners would be really helpful!

class DocumentRepository<T : ModelWithMetadata>(
        path: List<String>,
        private val model: Class<T>) {

    private var documentReference: DocumentReference

    val observable: Observable<T>

    private var emitter: ObservableEmitter<T>? = null
    private lateinit var item: T


    init {
        documentReference = FirebaseFirestore.getInstance().collection(path[0]).document(path[1])
        for (i in 2..path.lastIndex step 2)
            documentReference = documentReference.collection(path[i]).document(path[i + 1])

        observable = Observable.create(this::listenChanges)
    }

    private fun listenChanges(emitter: ObservableEmitter<T>) {
        this.emitter = emitter
        documentReference.addSnapshotListener { documentSnapshot, _ ->
            item = documentSnapshot.toObject(this.model)
            this.emitter?.onNext(item)
        }
    }

    fun get() {
        emitter?.onNext(item)
    }

    fun put(item: T) {
        item.updatedAt = TimeExtension.now()
        documentReference.set(item)
    }

    fun delete(item: T) {
        documentReference.delete()
    }
}
JaviOverflow
  • 1,434
  • 2
  • 14
  • 31
  • 1
    Use a function to create your observable instead of a class. See this old example to get the idea: http://www.andreamaglie.com/2015/rxjava-listener-to-observable/ – ESala Jan 15 '18 at 12:58

1 Answers1

1

documentReference.addSnapshotListener returns a ListenerRegistration which allows you to call ListenerRegistration#remove to remove the listener.

And, Emitter#setCancellable allows you to clean up resources, in this case detaching a listener, when the Observable is unsubscribed.

So your listenChanges would look like this:

private fun listenChanges(emitter: ObservableEmitter<T>) {
  this.emitter = emitter
  val registration = documentReference.addSnapshotListener { documentSnapshot, _ ->
    item = documentSnapshot.toObject(this.model)
    this.emitter?.onNext(item)
  }
  emitter.setCancellable { registration.remove() }
}
Aaron He
  • 5,509
  • 3
  • 34
  • 44
  • Can you help me with the listenChanges? I'm new in kotlin. What do you pass there for ObservableEmitter param? – vihkat Mar 10 '18 at 14:53
  • T is a generic class, in this case, of the object you want to emit. Instead of using, for instance, List, you use T and then you give the class when instantiating the object of this class. That allows you to use this class (DocumentRepository) for any class you want to emit. – JaviOverflow Mar 12 '18 at 08:23