Why can I get the correct result when I use the function writes to a local variable in Compose ? - android-studio

The Code A is based the article.
I was told that if the function writes to a local variable, this code will not be thread-safe or correct, and I will get the wrong result.
1: I test it with the Code A, but I always get the correct result, why?
2: Is the Code B correct?
Code A
#Composable
fun ListWithBug(myList: List<String>) {
var items = 0
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Column {
for (item in myList) {
Text("Item: $item")
items++ // Avoid! Side-effect of the column recomposing.
}
}
Text("Count: $items")
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
ListWithBug(mutableListOf("a","b","c","d","e","f","g","h","k","j"))
}
}
}
}
Code B
#Composable
fun ListWithBug(myList: List<String>) {
var items by remember { mutableStateOf(0) }
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Column {
for (item in myList) {
Text("Item: $item")
items++ // Avoid! Side-effect of the column recomposing.
}
}
Text("Count: $items")
}
}

It will not work as expected under practical scenarios. It might be working for you because you are testing it in isolation. Whenever a Composable recomposes, it will re-initialise all the variables that are declared in it. Hence, if something s=triggers a recomposition of the Composable, it will re-initialise the items var to 0. I said it would not work in practical application because over that place, there's a pool of composables the user is interacting with. A press of a button could cause multiple recompositions so it is not at all safe to maintain this state in the local composable.
The second approach MIGHT be fine, but is not at all recommended since it can cause state inconsistency among other composables, because ideally, you should store all of your UI state in a viewmodel. It acts as a single source of truth and all the composables can read the state from a single place that way.
You can learn about state hoisting in order to use viewmodels consistently with Compose. Just check out this codelab.
Maybe specifically this page, but surely give a go to the codelab on the whole.

Related

Coordinating emission and subscription in Kotlin coroutines with hot flows

I am trying to design an observable task-like entity which would have the following properties:
Reports its current state changes reactively
Shares state and result events: new subscribers will also be notified if the change happens after they've subscribed
Has a lifecycle (backed by CoroutineScope)
Doesn't have suspend functions in the interface (because it has a lifecycle)
The very basic code is something like this:
class Worker {
enum class State { Running, Idle }
private val state = MutableStateFlow(State.Idle)
private val results = MutableSharedFlow<String>()
private val scope = CoroutineScope(Dispatchers.Default)
private suspend fun doWork(): String {
println("doing work")
return "Result of the work"
}
fun start() {
scope.launch {
state.value = State.Running
results.emit(doWork())
state.value = State.Idle
}
}
fun state(): Flow<State> = state
fun results(): Flow<String> = results
}
The problems with this arise when I want to "start the work after I'm subscribed". There's no clear way to do that. The simplest thing doesn't work (understandably):
fun main() {
runBlocking {
val worker = Worker()
// subscriber 1
launch {
worker.results().collect { println("received result $it") }
}
worker.start()
// subscriber 2 can also be created "later" and watch
// for state()/result() changes
}
}
This prints only "doing work" and never prints a result. I understand why this happens (because collect and start are in separate coroutines, not synchronized in any way).
Adding a delay(300) to coroutine inside doWork "fixes" things, results are printed, but I'd like this to work without artificial delays.
Another "solution" is to create a SharedFlow from results() and use its onSubscription to call start(), but that didn't work either last time I've tried.
My questions are:
Can this be turned into something that works or is this design initially flawed?
If it is flawed, can I take some other approach which would still hit all the goals I have specified in the beginning of the post?
Your problem is that your SharedFlow has no buffer set up, so it is emitting results to its (initially zero) current collectors and immediately forgetting them. The MutableSharedFlow() function has a replay parameter you can use to determine how many previous results it should store and replay to new collectors. You will need to decide what replay amount to use based on your use case for this class. For simply displaying latest results in a UI, a common choice is a replay of 1.
Depending on your use case, you may want to give your CoroutineScope a SupervisorJob() in its context so it isn't destroyed by any child job failing.
Side note, your state() and results() functions should be properties by Kotlin convention, since they do nothing but return references. Personally, I would also have them return read-only StateFlow/SharedFlow instead of just Flow to clarify that they are not cold.

the right way to return a Single from a CompletionStage

I'm playing around with reactive flows using RxJava2, Micronaut and Cassandra. I'm new to rxjava and not sure what is the correct way to return a of List Person in the best async manner?
data is coming from a Cassandra Dao interface
public interface PersonDAO {
#Query("SELECT * FROM cass_drop.person;")
CompletionStage<MappedAsyncPagingIterable<Person>> getAll();
}
that gets injected into a micronaut controller
return Single.just(personDAO.getAll().toCompletableFuture().get().currentPage())
.subscribeOn(Schedulers.io())
.map(people -> HttpResponse.ok(people));
OR
return Single.just(HttpResponse.ok())
.subscribeOn(Schedulers.io())
.map(it -> it.body(personDAO.getAll().toCompletableFuture().get().currentPage()));
OR switch to RxJava3
return Single.fromCompletionStage(personDAO.getAll())
.map(page -> HttpResponse.ok(page.currentPage()))
.onErrorReturn(throwable -> HttpResponse.ok(Collections.emptyList()));
Not a pro of RxJava nor Cassandra :
In your first and second example, you are blocking the thread executing the CompletionStage with get, even if you are doing it in the IO thread, I would not recommand doing so.
You are also using a Single wich can emit, only one value, or an error. Since you want to return a List, I would sugest to go for at least an Observable.
Third point, the result from Cassandra is paginated, I don't know if it's intentionnaly but you list only the first page, and miss the others.
I would try a solution like the one below, I kept using the IO thread (the operation may be costly in IO) and I iterate over the pages Cassandra fetch :
/* the main method of your controller */
#Get()
public Observable<Person> listPersons() {
return next(personDAO.getAll()).subscribeOn(Schedulers.io());
}
private Observable<Person> next(CompletionStage<MappedAsyncPagingIterable<Person>> pageStage) {
return Single.fromFuture(pageStage.toCompletableFuture())
.flatMapObservable(personsPage -> {
var o = Observable.fromIterable(personsPage.currentPage());
if (!personsPage.hasMorePages()) {
return o;
}
return o.concatWith(next(personsPage.fetchNextPage()));
});
}
If you ever plan to use reactor instead of RxJava, then you can give cassandra-java-driver-reactive-mapper a try.
The syntax is fairly simple and works in compile-time only.

Infinite Observable to HashMap as Observable

I am still struggling with some of the basics of RxJava and would greatly appreciate some help.
I have an infinite, hot observable which emits regularly tagged events (captures in a simple class having a name (tag) and some properties). The tags are finite (in this case about 10 distinct tags) but the event specifics are every time different (a time stamp, say).
What I am trying to do now is to create a HashMap with the tags as key and the events as the entry, such that the HashMap itself becomes an infinite observable which emits the HashMap with every change.
So far, I used a Subject to subscribe to the original observable and to emit the HashMap, but I also saw the ".toMap" method. However, I cannot figure out how use that method with an infinite observable source and emit with every change. From the documentation it is not even clear to me whether this would be possible at all.
If it's not possible, is there another way, aside from using Subjects, to achieve the same? I want to keep this lean, and Subject seems rather heavy.
Here is my code convert it to a Map or Observable<Map>. But I don't know why you need to do this.
fun <T, K> Observable<T>.toInfiniteMap(keySelector: (T) -> K): Map<K, Observable<T>> {
val map = ConcurrentHashMap<K, Observable<T>>()
this.subscribeOn(Schedulers.newThread())
.doOnNext { println(it) }
.groupBy(keySelector)
.doOnNext { map.put(it.getKey(), it) }
.subscribe()
return map
}
fun <T, K> Observable<T>.toInfiniteMapObservable(keySelector: (T) -> K):
Observable<Map<K, Observable<T>>> {
val map = ConcurrentHashMap<K, Observable<T>>()
return this.subscribeOn(Schedulers.newThread())
.doOnNext { println(it) }
.groupBy(keySelector)
.doOnNext { map.put(it.getKey(), it) }
.map { map }
}

How to reuse code block which describe similiar ant build logic in groovy?

How to reuse code block which describe similiar ant build logic in groovy?
If we have build logic which was implemented by Groovy AntBuilder, just like code below:
ant.someTask(attr1:value1, attr2:value2) {
configuration1(param1:args1, param2:args2){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
configuration2(param3:args3, param4:args4){
similiarStructure(additionalArgs:aaa){
setting1(param5:value5) {
//...blah blah blah
}
//further more settings, may be or may be not the same with similiarStructure below
}
}
}
Are there any ways to reuse Groovy AntBuilder code block, which could brief the statment in configuration2 ?
I've try to predefine closures and inject them in both configuration,
but it fails with property not found exception while initializing closure.
I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you want a more general purpose solution that allows you to share the whole of the similarStructure block, you need to perform some more advanced work. The trick is to ensure that the delegate of the shared configuration closure is set appropriately:
def sharedConfig = {
similarStructure(additionalArgs:aaa) {
setting1(param5:value5) {
//...blah blah blah
}
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2){
applySharedConfig(delegate, sharedConfig)
}
configuration2(param3:args3, param4:args4){
applySharedConfig(delegate, sharedConfig)
}
}
void applySharedConfig(builder, config) {
def c = config.clone()
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = builder
c.call()
}
Although the applySharedConfig() method seems ugly, it can be used to share multiple configurations across different tasks.
One thing to bear in mind with this solution is that the resolveStrategy of the closure can be very important. I think both DELEGATE_FIRST and OWNER_FIRST (the default) will work fine here. If you run into what appear to be name resolution problems (missing methods or properties) you should try switching the resolution strategy.
I'll provide two answers so you can select which one is more appropriate for your use case and test it. The solutions depend on at what level you want the shared config.
If you are happy to simply share the closure that goes with similarStructure, then the solution is straightforward:
def sharedConfig = {
setting1(param5:value5) {
//...blah blah blah
}
}
ant.someTask(attr1: value1, attr2: value2) {
configuration1(param1:args1, param2:args2) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
configuration2(param3:args3, param4:args4) {
similarStructure(additionalArgs:aaa, sharedConfig)
}
}
The method that is similarStructure should ensure that the sharedConfig closure is properly configured. I haven't tested this, so I'm not entirely sure. The disadvantage of this approach is that you have to duplicate the similarStructure call with its arguments.

Best groovy closure idiom replacing java inner classes?

As new to groovy...
I'm trying to replace the java idiom for event listeners, filters, etc.
My working code in groovy is the following:
def find() {
ODB odb = ODBFactory.open(files.nodupes); // data nucleus object database
Objects<Prospect> src = odb.getObjects(new QProspect());
src.each { println it };
odb.close();
}
class QProspect extends SimpleNativeQuery {
public boolean match(Prospect p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
}
Now, this is far from what I'm used to in java, where the implementation of the Query interface is done right inside the odb.getObjects() method. If I where to code "java" I'd probably do something like the following, yet it's not working:
Objects<Prospect> src = odb.getObjects( {
boolean match(p) {
if (p.url) {
return p.url.endsWith(".biz");
}
return false;
}
} as SimpleNativeQuery);
Or better, I'd like it to be like this:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
However, what groovy does it to associate the "match" method with the outer script context and fail me.
I find groovy... groovy anyways so I'll stick to learning more about it. Thanks.
What I should've asked was how do we do the "anonymous" class in groovy. Here's the java idiom:
void defReadAFile() {
File[] files = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.getPath().endsWith(".biz");
}
});
}
Can groovy be as concise with no additional class declaration?
I think it would have helped you to get answers if you'd abstracted the problem so that it didn't rely on the Neodatis DB interface -- that threw me for a loop, as I've never used it. What I've written below about it is based on a very cursory analysis.
For that matter, I've never used Groovy either, though I like what I've seen of it. But seeing as no one else has answered yet, you're stuck with me :-)
I think the problem (or at least part of it) may be that you're expecting too much of the SimpleNativeQuery class from Neodatis. It doesn't look like it even tries to filter the objects before it adds them to the returned collection. I think instead you want to use org.neodatis.odb.impl.core.query.criteria.CriteriaQuery. (Note the "impl" in the package path. This has me a bit nervous, as I don't know for sure if this class is meant to be used by callers. But I don't see any other classes in Neodatis that allow for query criteria to be specified.)
But instead of using CriteriaQuery directly, I think you'd rather wrap it inside of a Groovy class so that you can use it with closures. So, I think a Groovy version of your code with closures might look something like this:
// Create a class that wraps CriteriaQuery and allows you
// to pass closures. This is wordy too, but at least it's
// reusable.
import org.neodatis.odb.impl.core.query.criteria;
class GroovyCriteriaQuery extends CriteriaQuery {
private final c;
QProspect(theClosure) {
// I prefer to check for null here, instead of in match()
if (theClosure == null) {
throw new InvalidArgumentException("theClosure can't be null!");
}
c = theClosure;
}
public boolean match(AbstractObjectInfo aoi){
//!! I'm assuming here that 'aoi' can be used as the actual
//!! object instance (or at least as proxy for it.)
//!! (You may have to extract the actual object from aoi before calling c.)
return c(aoi);
}
}
// Now use the query class in some random code.
Objects<Prospect> src = odb.getObjects(
new GroovyCriteriaQuery(
{ it.url.endsWith(".biz") }
)
)
I hope this helps!
I believe your real question is "Can I use closures instead of anonymous classes when calling Java APIs that do not use closures". And the answer is a definite "yes". This:
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
should work. You write "However, what groovy does it to associate the "match" method with the outer script context and fail me". How exactly does it fail? It seems to me like you're having a simple technical problem to get the solution that is both "the groovy way" and exactly what you desire to work.
Yep, thanks y'all, it works.
I also found out why SimpleNativeQuery does not work (per Dan Breslau).
I tried the following and it worked wonderfully. So the idiom does work as expected.
new File("c:\\temp").listFiles({ it.path.endsWith(".html") } as FileFilter);
This next one does not work because of the neodatis interface. The interface does not enforce a match() method! It only mentions it in the documentation yet it's not present in the class file:
public class SimpleNativeQuery extends AbstactQuery{
}
Objects<Prospect> src = odb.getObjects(
{ it.url.endsWith(".biz") } as SimpleNativeQuery
);
In the above, as the SimpleNativeQuery does not have a match() method, it makes it impossible for the groovy compiler to identify which method in the SimpleNativeQuery should the closure be attached to; it then defaults to the outer groovy script.
It's my third day with groovy and I'm loving it.
Both books are great:
- Groovy Recipes (Scott Davis)
- Programming Groovy (Venkat Subramaniam)

Resources