Chaining Calls on RxJava - android

There are cases when I need to chain RxJava calls.
The simplest one:
ViewModel:
fun onResetPassword(email: String) {
...
val subscription = mTokenRepository.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
//UI update calls
)
...
}
My Repository:
fun resetPassword(email: String): Single<ResetPassword> {
return Single.create { emitter ->
val subscription = mSomeApiInterface.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
emitter.onSuccess(...)
}, { throwable ->
emitter.onError(throwable)
})
...
}
}
My Question
Do I need to Add:
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for both calls to avoid any app freeze? or the second one for API call is enough?

No, you don't need to add
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for the repo and the viewmodel.
.observeOn usually should be called right before handling the ui rendering. So usually, you'll need it in the ViewModel right before updating the ui or emitting the LiveData values.
Also, you properly don't need to subscribe to mSomeApiInterface in your repo, I think it would be better off to just return in as it's from your method up the chain, somthing like this:
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email);
}
and if you have any mapping needed you can chain it normally
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email)
.map{it -> }
}
This way you can write your ViewModel code as follow
fun onResetPassword(email: String) {
...
// note the switcing between subscribeOn and observeOn
// the switching is in short: subscribeOn affects the upstream,
// while observeOn affects the downstream.
// So we want to do the work on IO thread, then deliver results
// back to the mainThread.
val subscription = mTokenRepository.resetPassword(email)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//UI update calls
)
...
}
This will run the API request on the io thread, will returning the result on the mainThread, which is probably what you want. :)
This artical has some good examples and explanations for subscribeOn and observeOn, I strongly recommend checking it.

Observable<RequestFriendModel> folderAllCall = service.getUserRequestslist(urls.toString());
folderAllCall.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(result -> result.getRequested())
.subscribe(this::handleResults, this::handleError);
private void handleResults(List<Requested> folderList) {
if (folderList != null && folderList.size() != 0) {
usersList.addAll(folderList);
}
adapter.notifyDataSetChanged();
}
}
private void handleError(Throwable t) {
Toast.makeText(getContext(),t.getMessage(),Toast.LENGTH_LONG).show();
}
in interface:
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET
Observable<RequestFriendModel> getUserRequestslist(#Url String url);
POJO model :
public class RequestFriendModel {
#SerializedName("requested")
#Expose
private List<Requested> requested = null;
public List<Requested> getRequested() {
return requested;
}
public void setRequested(List<Requested> requested) {
this.requested = requested;
}
}

Related

Abstract coroutines structure

With RxJava we can do something like this:
BaseViewModel
protected void subscribe(Completable completable, MutableLiveData<Response> response) {
mDisposable.add(
completable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe(disposable -> response.setValue(Response.loading()))
.doFinally(() -> response.setValue(Response.idle()))
.subscribe(
() -> response.setValue(Response.success(true)),
e -> response.setValue(Response.error(e))
)
);
}
protected <T> void subscribe(Single<T> single, MutableLiveData<Response> response) {
mDisposable.add(
single.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe(disposable -> response.setValue(Response.loading()))
.doFinally(() -> response.setValue(Response.idle()))
.subscribe(
result -> response.setValue(Response.success(result)),
e -> response.setValue(Response.error(e))
)
);
}
Then, from repository we getting Single/Complete and pass it to our custom subscribe(), then we get generic Result with data(optional), very easy way to work with asynchronous requests.
How we can abstract coroutines with similar structure, instead of write Launch in every method in ViewModel and try/catch error manually?
Instead of closely following the code you already have with minimal adaptations, I suggest you review your design altogether when migrating to coroutines.
One important principle embedded into coroutines is structured concurrency. This isn't just about the coroutine scopes and cancellation, it is also about the use of futures by any name (be it CompletionStage, Deferred, Task, Single or any other). According to structured concurrency, a future is basically equivalent to a live thread that has no defined scope. You should avoid them.
Instead you should have clearly delineated places in the code that launch new concurrent work contained within a single top-level block of code provided at the launch site.
So far, that implies that you do have a launch block at each entry point into your code from the Android framework, and that's a lot of places due to the nature of the callback-oriented programming model.
However, everything within that block should be coded according to structured concurrency. If you have just one network call to make, your code is entirely sequential: make the call, get the response, process it. The network calls themselves become suspend functions that complete with the result of the call and do not accept callbacks. All the traditional design patterns from the world of blocking calls apply here.
See here for an intro to using coroutines with LiveData, it may help you map your design to the coroutine-oriented one:
https://developer.android.com/topic/libraries/architecture/coroutines#livedata
You are probably looking for something like this
CoroutineWrapper
fun <T> ViewModel.apiCx(context: CoroutineContext = Dispatchers.Default, init: suspend CxWrapper<T>.() -> Unit) {
val wrap = CxWrapper<T>(context)
wrap.launch {
try {
init.invoke(wrap)
callCx(wrap)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun <T> callCx(wrap: CxWrapper<T>) {
val response: Response<T>? = wrap.request
response?.let {
if (it.isSuccessful) {
wrap.success(it.body())
} else {
wrap.fail(Pair(it.code(), it.message()))
}
}
}
class CxWrapper<T>(override val coroutineContext: CoroutineContext) : CoroutineScope {
var request: Response<T>? = null
internal var success: (T?) -> Unit = {}
internal var fail: (Pair<Int, String?>) -> Unit = {}
fun success(onSuccess: (T?) -> Unit) {
success = onSuccess
}
fun error(onError: (Pair<Int, String?>) -> Unit) {
fail = onError
}
}
you can have this as a separate helper class and to use this from your ViewModel
apiCx<YourModelClass> {
request = yourApiCall()
success { yourModelClass ->
Log.d(TAG, "success")
}
error {
Log.e(TAG, "error")
}
}
You would just do the same, just adapted to coroutines. Just replace the different stream types with the suspension methods you need.
protected inline fun <T> MutableLiveData<Response>.subscribe(single: suspend () -> T) {
viewModelScope.launch {
try {
value = Response.loading()
value = withContext(Dispatchers.IO) {
Response.success(single())
}
} catch(e: Throwable) {
value = Response.error(e)
} finally {
value = Response.idle()
}
}
To use it just call with the livedata as receiver
responseLiveData.subscribe<T> {
singleFromRepo()
}
responseLiveData.subscribe<Unit> {
completableFromRepo()
}

RxJava2 - How to know when all concatMapSingle operations finished on iterating Observable

I have the method below:
private fun bindUploadPhotos(uploadPhotoCommands: List<UploadPhotoCommand>): Disposable {
return Observable.fromIterable(uploadPhotoCommands)
.concatMapSingle { param ->
requestUploadPhoto.getSingle(param)
}
.doFinally {
onAllPhotosUploaded()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
onPhotoUploaded()
}
}
I upload photos sequentially. I expect doFinally to execute after all photos are uploaded successfully. However, it executes immediately.
I am not a RxJava2 master, so I gladly take your different approaches to achieve my purpose.
onComplete called when all your photos uploaded. so call onAllPhotosUploaded there
private fun bindUploadPhotos(uploadPhotoCommands: List<UploadPhotoCommand>): Disposable {
return Observable.fromIterable(uploadPhotoCommands)
.flatMapSingle { param ->
requestUploadPhoto.getSingle(param)
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({onPhotoUploaded},{},{onAllPhotosUploaded()})
}

Worker queue using RxJava

I want to create worker queue using RxJava: I have a single thread doing some work, and I want to guarantee that no other job will be executed until we have finished/failed the current job.
My solution is simply to block the observable and wait for the result:
fun foo() : Observable<Foo> {
return Observable.unsafeCreate { subscriber ->
handlerThread.post {
val answer = object.performSomeJob(whatever)
.flatMap { object.performAnotherJob(whatever) }
.flatMap { object.performLastJob(whatever) }
.blockingFirst()
subscriber.onNext(answer)
subscriber.onComplete()
}
}
}
You may argue that there is no need to use RxJava since everything's synchronous. That's true for this particular method, but:
I want to avoid 'callback hell': there are three methods, each of which is taking callback and I use RxJava to chain them
I use Rx further on in the caller method.
I know that blocking is generally considered as an anti-pattern, so can I do better in my case?
you can use concat to perform work sequentially on some thread:
fun foo(): Observable<Foo> {
return performSomeJob(whatever)
.concatMap { performAnotherJob(whatever) }
.concatMap { performLastJob(whatever) }
.subscribeOn(Schedulers.newThread())
}
You can schedule all your work on one single-threaded Scheduler such as
#NonNull
public static Scheduler single()
Returns a default, shared, single-thread-backed Scheduler instance for work requiring strongly-sequential execution on the same background thread.
fun foo(): Observable<Foo> =
Observable.fromCallable { object.performSomeJob(whatever) }
.subscribeOn(Schedulers.single())
.observeOn(Schedulers.single())
.flatMap { object.performAnotherJob(whatever) }
.flatMap { object.performLastJob(whatever) }

RxJava Observable.create wrapping observable subscriptions

I used Observable.create so I could notify the subscriber when certain data was available. I am a little uncertain of subscribing to observables inside of my create method. Are these nested subscriptions going to give me any sort of issue? I'm not completely familiar with creating observables using Observable.create so I wanted to make sure I'm not doing anything out of the ordinary or misusing it. Thank you in advance!
abstract class NetworkResource<ApiType, DbType> constructor(private val schedulerProvider: SchedulerProvider) {
abstract fun fetchFromApi(): Single<ApiType>
abstract fun fetchFromDb(): Observable<Optional<DbType>>
abstract fun saveToDb(apiType: ApiType?)
abstract fun shouldFetchFromApi(cache: DbType?): Boolean
fun fetch(): Observable<Optional<DbType>> {
return Observable.create<Optional<DbType>> {
val subscriber = it
fetchFromDb()
.subscribe({
subscriber.onNext(it)
if(shouldFetchFromApi(it.get())) {
fetchFromApi()
.observeOn(schedulerProvider.io())
.map {
saveToDb(it)
it
}
.observeOn(schedulerProvider.ui())
.flatMapObservable {
fetchFromDb()
}
.subscribe({
subscriber.onNext(it)
subscriber.onComplete()
})
}
else {
subscriber.onComplete()
}
})
}
}
}
Yes, it will cause an issues.
First, it is not idiomatic to nest Observable like this, one of the strengths of Reactive approach, is composing Observables, and thus have single clean stream. with this way, you are breaking the chain, and the immediate result is intertwined code which is harder to read, and more code to wire up the notification events, basically it is like wrapping async callback methods with Observable.
here as you have already reactive components you can simply compose them instead of treating them with callback approach.
Second, as a result of breaking the chain, the most sever and immediate one - unsubscribing the outer Observable will not affect automatically the inner Observable. same goes for trying to add subscribeOn() and with different scenario where backpressure is important it's also apply.
an composing alternative might be something like this:
fun fetch2(): Observable<Optional<DbType>> {
return fetchFromDb()
.flatMap {
if (shouldFetchFromApi(it.get())) {
fetchFromApi()
.observeOn(schedulerProvider.io())
.doOnSuccess { saveToDb(it) }
.observeOn(schedulerProvider.ui())
.flatMapObservable {
fetchFromDb()
}
} else {
Observable.empty()
}
}
}
if from some reason, you want in any case the first fetchFromDb() result to be emitted separately, you can also do it using publish() with selector:
fun fetch2(): Observable<Optional<DbType>> {
return fetchFromDb()
.publish {
Observable.merge(it,
it.flatMap {
if (shouldFetchFromApi(it.get())) {
fetchFromApi()
.observeOn(schedulerProvider.io())
.doOnSuccess { saveToDb(it) }
.observeOn(schedulerProvider.ui())
.flatMapObservable {
fetchFromDb()
}
} else {
Observable.empty()
}
})
}
}

RxAndroid - retry observable on click

I'm using rxAndroid and rxKotlin in my Android app to handle network requests asynchronously. Now I would like to retry a failed network request only after click on Snackbar button.
My code now:
val citiesService = ApiFactory.citiesService
citiesService.cities()
.subscribeOn(Schedulers.newThread()) // fetch List<String>
.flatMap { Observable.from(it) } // convert to sequence of String
.flatMap { city ->
citiesService.coordinates(city) // fetch DoubleArray
.map { City(city, it) } // convert to City(String, DoubleArray)
}
.toList()
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
listView.setOnItemClickListener { adapterView, view, position, id ->
onItemClick(it[position])
}
}
.map { it.map { it.getName(activity) } }
.subscribe(
{ listAdapter = setupAdapter(it) },
{ showErrorSnackbar() } // handle error
)
fun showErrorSnackbar() {
Snackbar.make(listView, getString(R.string.not_available_msg), Snackbar.LENGTH_INDEFINITE)
.setAction(getString(R.string.snack_retry_btn), {
// retry observable
})
.show()
}
Cities interface for retrofit:
interface CitiesService {
#GET("api/v1/cities")
fun cities(): Observable<List<String>>
#GET("api/v1/cities/{city}/coordinates")
fun coordinates(#Path("city") city: String): Observable<DoubleArray>
}
Api factory:
object ApiFactory {
val citiesService: CitiesService
get() = retrofit.create(CitiesService::class.java)
private val retrofit: Retrofit
get() = Retrofit
.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
}
How can I restart the observable in such way?
I can suggest you truly reactive way instead of imperative way.
Insert this code right after subscribe() method:
.retryWhen(retryHandler ->
retryHandler.flatMap(nothing -> retrySubject.asObservable()))
.subscribe()
Where update subject is just:
#NonNull
private final PublishSubject<Void> retrySubject = PublishSubject.create();
And on snackbar click call this method:
public void update() {
retrySubject.onNext(null);
}
Everything above the retryWhen method will be literally redone.
Though with this approach error will never go down to the subscriber, you can add error conditioning to the retryHandler flat map, but this is another story.
P.S. sorry, this was Java code with retrolambdas, but you'll easily convert this to Kotlin.

Categories

Resources