简体   繁体   中英

Is it possible for this Android LiveData/Threading code to give some kind of concurrency issue or unexpected result?

I have some similar code throughout my project that is inside of my ViewModels which subscribes to an RxJava observable that is subscribedOn Schedulers.computation(). I have a MutableLiveData<Integer> isLoadedLiveData object that posts an updated int flag that will be observed in my activity.

Basically, in this ViewModel , if 3 subscriptions finish, then the isLoadedLiveData will be equal to 3 because each subscriptions adds to increments the int flag value of isLoadedLiveData . And in my LiveData observer of isLoadedLiveData in my activity, I then set up the views of that Activity once the int value is equal 3. So it lets me know that the ViewModel data is ready to go and each needed piece of data can be returned from each repsective getter. I do this so I don't need a bunch of LiveData objects in my Activity and can instead just have one flag that tells me when all my separate data is loaded. Here is a section of the code in my ViewModel :

Disposable disposable1 = this.thingRepository.getThingSingle()
            .observeOn(Schedulers.computation())
            .subscribe(thing -> {
                name = thing.getName();
                abbrev = thing.getAbbrev();
                stuff = thing.getStuff();
                loaded++;
                isLoadedLiveData.postValue(loaded);
            });

Now I'll preface this by saying I am not well versed in java/android concurrency, so I am not exactly sure, but I wouldn't think this code could give me some kind of problem. But I am not 100% sure. Could this possibly be problematic in certain situations, or will the sequence of code and threading not be an issue?

The data like name , abbrev , and stuff which are fields of my ViewModel are all returned to my Activity eventually (just through simple getters, no observers). Will these pieces of data always be correctly updated and safe to access from my Activity on the MainThread because the int flag being posted to isLoadedLiveData always occurs after the data is updated on the background threads. I am not completely sure because these values are being updated on a background thread and then the main thread accesses it. I don't know enough about concurrency.

This doesn't have much to do with RxJava but that is how I handle my threading in this case. It is more to do with Java Threading/Android Threading/LiveData in general. I am not completely sure how LiveData .postValue works, but I would assume it gets put in the main thread's Looper to execute my LiveData Observer callback with the posted value passed in. Will this no matter what always occur so that the values set above the isLoadedLiveData.postValue(loaded) are safe to access and correctly updated?

I appreciate any responses! Thanks.

Edit: Here is new code:

Disposable disposable1 = this.thingRepository.getThingSingle()
        .subscribeOn(Schedulers.computation())
        .doOnSuccess(thing -> {
            name = thing.getName();
            abbrev = thing.getAbbrev();
            heavyOperationResult = heavyOpperation(thing);
            stuff = thing.getStuff();
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(thing -> {
            loaded++;
            isLoadedLiveData.setValue(loaded);
        });

I added .subscribeOn(Schedulers.computation()) even though my repository returns a Single that is already subscribed on Schedulers.computation() just to express what thread it was subscribed on for the purposes of this example. I also added the heavyOperation(thing) just to show that I need it in the background because I do some computation that could take too long for the main UI thread.

I disagree with that code snippet, you are mis-using Rx in my opinion.

Two things:

  1. doOnSuccess is meant to be used for side-effects, not for doing heavy computations.

  2. Do NOT take the state out of the Rx stream, pass it downstream instead. This statement name = thing.getName(); and others like that inside doOnSuccess are really dangerous because you could have several streams modifying the same state in different threads.

What you want is to actually pass the state downstream and eventually publish it in LiveData, then you observe it as it changes.

 Disposable disposable = thingRepository.getThingSingle()
            .subscribeOn(Schedulers.io())
            .flatMap(thing ->
                    Single.fromCallable(() -> heavyOperation(thing))
                            .map(heavyOperationResult -> new Pair<>(thing, heavyOperationResult))
                            .subscribeOn(Schedulers.computation()))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(pair -> {
                Thing thing = pair.getValue0();
                HeavyOperationResult heavyOperationResult = pair.getValue1();
                thingLiveData.setValue(thing);
                heavyOperationLiveData.setValue(heavyOperationResult);
            });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM