Dataflow async
Library ID dataflow-async
Latest version 1.0.5

Listening attributes

Recall that an attribute is internally a Field<AttributeData<T>>. There isn't a direct way to listen to attributes, but you use the same infrastructure that is already present for fields. For more information see the dataflow reference for listening fields.

How to listen

link

Attributes expose the underlying fields through the asField() function, that returns a Field<AttributeData<T>>. You can then listen for changes in this field.

There is also a convenience extension function that directly accepts an attribute and that listens for the underlying field.

Example:

import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.async.* import com.femastudios.dataflow.async.util.* fun main() { val attr = attributeOf { 10 } lifecycle { listen(attr) { when(it) { is Loaded -> println("LOADED! Value is: ${it.value}") is Loading -> println("Loading...") is Error -> println("Error loading data! ${it.displayableMessage}") } } Thread.sleep(100) //Wait to allow first computation println("recompute()") attr.recompute() Thread.sleep(100) //Wait to allow second computation } }
Attribute<Integer> attr = Attribute.of(wc => 10); LifecycleOwner.lifecycle(lc -> { //In java it's just easier to call `asField()` then it is to call the extension function lc.listen(attr.asField(), v -> { if(v instanceof Loaded) { System.out.println("LOADED! Value is: " + v.getValue()); } else if(v instanceof Loading) { System.out.println("Loading..."); } else if(v instanceof Error) { System.out.println("Error loading data! " + v.getDisplayableMessage()); } else { throw new IllegalStateException(); //can never reach here because AttributeData and NotLoaded are sealed classes } }); Thread.sleep(100); //Wait to allow first computation System.out.println("recompute()"); attr.recompute(); Thread.sleep(100); //Wait to allow second computation });

As you can see using this method you can verify the status of the attribute, and you may as check for custom statuses.

If you don't need the distinction between the not loaded statuses, you can simply call the valueOrNull() function that will return a Field<T?> whose value will be null when the status is not loaded.

Example:

lifecycle { listen(attr.valueOrNull()) { if(it == null) { println("Attribute value not available") } else { println(it) } } }
LifecycleOwner.lifecycle(lc -> { lc.listen(attr.valueOrNull(), v -> { if(v == null) { System.out.println("Attribute value not available"); } else { System.out.println(v); } }); });

Listening on another thread

link

If we need to do a lot of work on our listener we may consider doing it on another thread. Luckily it's very easy to do it in Dataflow! All you need to do is call the async() function on the wanted lifecycle owner and then proceed to listen for fields or attributes as usual.

Example:

import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.async.* import com.femastudios.dataflow.async.util.* fun main() { //With FlowStrategy.SYNCHRONOUS we ensure that when calling `recompute()` the calculation is done on the main thread val attr = attributeOf(flowStrategy = FlowStrategy.SYNCHRONOUS) { println("Task thread: " + Thread.currentThread().id) 10 } globalLifecycle.async().listen(attr) { //Some expensive computation println("Listener thread: " + Thread.currentThread().id) //Verify we are on a different thread } println("Main thread: " + Thread.currentThread().id) attr.recompute() Thread.sleep(100) //Allow for recompute() to run before terminating program }
//With FlowStrategy.SYNCHRONOUS we ensure that when calling `recompute()` the calculation is done on the main thread Attribute<Integer> attr = Attribute.of(FlowStrategy.SYNCHRONOUS, wc => { System.out.println("Task thread: " + Thread.currentThread().id); return 10; }); LifecycleOwnerUtils.async(globalLifecycle).listen(attr) { //Some expensive computation System.out.println("Listener thread: " + Thread.currentThread().id); //Verify we are on a different thread } System.out.println("Main thread: " + Thread.currentThread().id); attr.recompute(); Thread.sleep(100); //Allow for recompute() to run before terminating program