Using lifecycles

This way of listening fields has a very similar concept to the weak way of the previous page: we still register weak listeners inside the ListenersManager, but our reference are automatically kept inside the object's lifecycle. This is possible by implementing the LifecycleOwner interface. The callback is still called by the thread that triggered it.

The LifecycleOwner interface

link

This interface has two main functions to implement:

ListenerHolder is a particular class that holds the callback and some associated info; you cannot directly instantiate it.

This interface also has multiple versions of a listen() function: given its parameters it creates a ListenerHolder and calls attachToLifecycle(). After that it returns the created ListenerHolder.

Whenever you want to listen to a field and attach the callback to a LifecycleOwner all you have to do is call listen() on it passing the callback: everything else will automatically be taken care of.

This is the basic implementation of LifecycleOwner that already provides all the needed functions to work.

We can now see a brief example on an object extending BaseLifecycleOwner:

import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.util.* fun main() { class ObjectWithLifecycle : BaseLifecycleOwner() { } val f = mutableFieldOf(10) val obj = ObjectWithLifecycle() obj.listen(f) { println(it) } f.value = 42 }
class ObjectWithLifecycle extends BaseLifecycleOwner { } MutableField<Integer> f = MutableField.of(10) ObjectWithLifecycle obj = new ObjectWithLifecycle(); obj.listen(f, newData -> { System.out.println(newData); }); f.setValue(42);

A thing we should keep in mind is that calling listen() will also execute the callback the first time. If you do not want this behavior you can set the parameter callImmediately to false.

Please note that the callbacks listeners are registered and listening to changes until the garbage collector destroys the BaseLifecycleOwner. If you don't like this behavior, you can manually call stop() and destroy() methods.

Global lifecycle

link

If you need to register a listener for the whole duration of the program you can use a special lazily-instantiated LifecycleOwner instance, called global lifecycle:

import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.util.* fun main() { val f = mutableFieldOf("hello") globalLifecycle.listen(f) { println(it) } println("world") }
MutableField<String> f = MutableField.of("hello"); LifecycleOwner.globalLifecycle.listen(f, (v) -> { System.out.println(v); }); f.setValue("world");

You could technically remove listeners from the global lifecycle, but doing so is discouraged since the listener's actual lifecycle wouldn't actually be global.

Block lifecycle

link

Last but not least, we can call the lifecycle() function providing a lambda that is immediately evaluated and that accepts as its receiver the newly created LifecycleOwner; as soon as it terminates the LifecycleOwner and all its listeners are stopped.

This method is meant for short-lived listeners whose life-span isn't necessarily attached to another already existing object.

Example:

import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.util.* fun main() { val f = mutableFieldOf(1) f.value = 2 f.value = 3 lifecycle { listen(f) { print("$it ") } f.value = 4 f.value = 5 } f.value = 6 f.value = 7 //Outputs 3 4 5 }
MutableField<Integer> f = MutableField.of(1); f.setValue(2); f.setValue(3); LifecycleOwner.lifecycle(lc -> { lc.listen(f, v -> { System.out.print(v + " "); }); f.setValue(4); f.setValue(5); }); f.setValue(6); f.setValue(7); //Outputs 3 4 5

As soon as the lambda exits, all listeners are canceled.

You can pass an Executor to this function and obtain a new LifecycleOwner whose listeners will be called on the given executor.