RxJava2 - Subscribing PublishSubject - android

private val searchSubject = PublishSubject.create<Boolean>()
private val compositeDisposable = CompositeDisposable()
fun textChange(){
searSubject.onNext(true)
}
fun getSubject(){
compositeDisposable += searchSubject
.doOnNext {
if (it) showLoading()
}
.switchMap { searchGithubReposObservable() }
.subscribeWith(object : DisposableObserver<List<GithubRepo>>() {
override fun onNext(t: List<GithubRepo>) {
hideLoading()
adapter.items = t
}
override fun onComplete() {
}
override fun onError(e: Throwable) {
hideLoading()
}
})
}
searchGithubReposObservable is the fucntion that returns the Observable<List<GithubRepo>>
I searched the sample code in the github for studying RxJava.
However, I can't understand the above code.
I know that to receive data from PublishSubject, I need to subscribe it.
In the above code, I thought that subscribeWith subscribes searchGithubReposObservable()'s return Observable , but I could get the data from PublishSubject when the textchange() is called.
Why is it possible?

The start of your RX chain you are listening to the publish subject.
compositeDisposable += searchSubject
.doOnNext {
if (it) showLoading()
}
Each time you call method textChange() you push to searchSubject which fires the RX chain all over again trigging the switchmap.

Yes is it possible you can get the data when textchange() method is getting called i have implemented this type of functionality while i was typing text api got called on textchange and i received data Below code.
I have written please check
autocompletetextview.debounce(500L, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.filter { it.trim().isNotEmpty() || it.isEmpty() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
Observable.just(callapi here )
}
.subscribe({
it.subscribe({ serviceResponse ->
if (serviceResponse.meta.status == KeyUtils.HTTP_SUCCESS ||
serviceResponse.meta.status == KeyUtils.STATUS_META_ERROR) {
setSuccessResponse(serviceResponse, true)
} else {
setSuccessResponse(serviceResponse, false)
}
}, { throwable ->
setErrorResponse(throwable)
}).collect()

Related

How to force (return) to not work until after (retrofit) finished

hello I'm trying to study dataBinding, mvvm, retrofit and rxjava
in viewModel I used this code
private var mainRepository: MainRepository = MainRepository(NetManager(getApplication()))
val isLoading = ObservableField(false)
var mainModel = MutableLiveData<ArrayList<MainModel>>()
private val compositeDisposable = CompositeDisposable()
fun loadRepositories(id: Int, mainContract: MainContract) {
isLoading.set(true)
compositeDisposable += mainRepository
.getData(id, mainContract)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableObserver<ArrayList<MainModel>>() {
override fun onError(e: Throwable) {
//if some error happens in our data layer our app will not crash, we will
// get error here
}
override fun onNext(data: ArrayList<MainModel>) {
mainModel.value= data
}
override fun onComplete() {
isLoading.set(false)
}
})
}
and in the MainRepository I used the retrofit with RxJava code
private val model = ArrayList<MainModel>()
fun getData(id: Int, mainContract: MainContract): Observable<ArrayList<MainModel>> {
Api.getData.getMainCategory(id)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
model.clear()
model.addAll(it)
AppLogger.log("testingModel1", model.toString())
}, {
AppLogger.log("error", "Failed to load Category : $it")
mainContract.toast("Failed to load Category")
})
AppLogger.log("testingModel2", model.toString())
return Observable.just(model)
}
if you notified that I'm using log to see the output data
but what I see is that
AppLogger.log("testingModel2", model.toString())
and
return Observable.just(model)
are running before
Api.getData.getMainCategory(id)
so the output in Logcat testingModel2 first and it is empty then testingModel1 and it is have data
so the result data in
return Observable.just(model)
is nothing
I hope you understand ^_^
Thank you for help
do like:
fun getData(id: Int, mainContract: MainContract): Observable<ArrayList<MainModel>> {
return Api.getData.getMainCategory(id)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
}
but remember to subscribe to it later and add ErrorHandling
And about logs: the problem that actions in subscribe block runs only when Api.getData.getMainCategory(id) emit something, which could take a time.

How to use RxJava in a normal retrofit call?

Context: My current application uses normal retrofit calls in order to get data from the api. I really wanted to introduce RX into the calls but I don't have much experience with that. I read some things online and none of them show me a simple way to do this. I'll show you what I have.
Purpose: To turn what I have into RXJava
This is my code :
My generic perform call method that I want to convert into RXJava:
fun <T> performCall(call: Call<T>?, callback: OnRequestCallback<T>) {
call?.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
when (response.code()) {
200, 201 -> {
callback.onSuccess(response.body())
}
else -> {
callback.onError(response.errorBody())
}
}
return
}
override fun onFailure(call: Call<T>, t: Throwable) {
callback.onError(null)
Log.d("Network Manager Failure", t.localizedMessage)
}
})
}
Then to have a context from my activity I use this method that calls the perform call method:
fun <T> BaseActivity.callAPI(call: Observable<T>?, onSucceed: (T?) -> Unit, onError: (errorCode: String) -> Unit) {
NetworkManager.instance.performCall(call,
object : NetworkManager.OnRequestCallback<T> {
override fun onSuccess(body: T?) {
onSucceed(body)
}
override fun onError(errorResponseBody: ResponseBody?) {
JSONObject(errorResponseBody?.string()).let {
val errorCode = it.getJSONObject("meta").getString("errorCode")
when (errorCode) {
Errors.DEPRECATED_API_VERSION.name ->
onAppUpdateNeeded()
else -> onError(errorResponseBody)
}
}
}
})
}
Then the BaseActivitt.callApi() is used in every activity that needs api information, I now the use of view models + dagger is better but for now is what I have and I have to keep it.
Can someone show me how to turn this into an RXJava/Kotlin thing?
To be honest I don't like the idea of having these generic handlers. I had to work on a project that was written like that and it wasn't a nice experience: what happens if, for example, you want to handle the Errors.DEPRECATED_API_VERSION in a different way for a call?
Anyway, I would do something like this: from Retrofit return the Observable and then in the place where you need to make the call subscribe to the Observable.
val disposable = service
.yourApi()
.map { value ->
MyCommand.SuccessCommand(value)
}
.onErrorResumeNext { ex: Throwable -> YourObservableThatEmitsTheErrorCommandOrTheOnAppUpdateNeededCommand() }
.subscribe { command: MyCommand ->
when (command) {
is MyCommand.SuccessCommand -> {
}
is MyCommand.ErrorCommand -> {
}
is MyCommand.AppUpdateNeededCommand -> {
}
}
}
The command can be implemented something like
sealed class MyCommand {
class SuccessCommand<T> (value: T): MyCommand()
class ErrorCommand (val ex: Exception): MyCommand()
object AppUpdateNeededCommand: MyCommand()
}

Better way to chain rxjava2 calls with conditional operations

I have the following code, that does one single call, gets the result of the call, which is a boolean, then makes the second call if the result is false.
private fun linkEmailAndTextTogether(contactPhoneNumber: ContactPhoneNumbers,phoneNumber : PhoneNumber) {
val single = SingleOnSubscribe<Boolean> {
contactPhoneNumber.doesEmailContactExist(phoneNumber)
}
Single.create(single)
.subscribeOn(Schedulers.io())
.subscribeWith(object : SingleObserver<Boolean> {
override fun onSuccess(phoneNumberDoesExist: Boolean) {
if (!phoneNumberDoesExist) {
val completable = CompletableOnSubscribe {
contactPhoneNumber.linkEmailAndTextTogether(phoneNumber)
}
compositeDisposable.add(Completable.create(completable)
.subscribeOn(Schedulers.io())
.subscribe())
}
}
override fun onSubscribe(d: Disposable) {
compositeDisposable.add(d)
}
override fun onError(e: Throwable) {
Timber.e(e,e.localizedMessage)
}
})
}
It seems like there should be a more elegant way to do this in some kind of chain.
you could use the flatMap operator - the downside is that you won't know if the first or the second failed.
Single.just(phoneNumber)
.subscribeOn(Schedulers.io())
.map { it -> contactPhoneNumber.doesEmailContactExist(it) }
.flatMap { it ->
if (it) {
return#flatMap contactPhoneNumber.linkEmailAndTextTogether(phoneNumber)
}
Single.just(it)
}.subscribe({}, Throwable::printStackTrace);
This should help.
val single = SingleOnSubscribe<Boolean> {
getSingle()
}
Single.create(single).map({
if (it){
return#map getCompleteable()
}
return#map Completable.complete()
})

RXjava2 method in fromCallable not getting exceuted

I am new to using rxjava and I am trying to run a function in background using rxjava2 but the method is not called the code I am using is given below let me know if its the right way to execute a function in background:
Observable.fromCallable<OrderItem>(Callable {
saveToDb(existingQty, newOty, product_id)
}).doOnSubscribe {
object : Observable<OrderItem>() {
override fun subscribeActual(emitter: Observer<in OrderItem>?) {
try {
val orderItem = saveToDb(existingQty, newOty, product_id)
emitter?.onNext(orderItem)
emitter?.onComplete()
} catch (e: Exception) {
emitter?.onError(e)
}
}
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).doOnSubscribe {
object : Observer<OrderItem> {
override fun onComplete() {
}
override fun onNext(t: OrderItem) {
}
override fun onError(e: Throwable) {
}
override fun onSubscribe(d: Disposable) {
}
}
}
You are dong it wrong way. doOnSubscribe() operator is called when observable is subscribed using subscribe() method and you haven't subscribed the observable using subscribe() method.
You have called saveToDb method in callable, then why are you calling it in doOnSubscribe? it doesn't make sense.
You should have written following code:
Observable.fromCallable { saveToDb(existingQty, newOty, product_id) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ orderItem ->
// set values to UI
}, { e ->
// handle exception if any
}, {
// on complete
})
to work with your logic.
DoOnSubscribe means "do when someone subscribe to it". But there is no subscribe in your code. Maybe you want to use subsribe instead of doOnSubscribe

RxAndroid, How to detect if observable has finished emission

I am writing following code snippet to fetch list of saved food from firebase database and then using that list, I am again fetching individual food details from firebase database.
Following code working fine, except i am unable to figure out how to let second flatMap know that emission of first flatMap has finished(All food list has been processed). So I am unable to call onCompleted() method hence unable to detect when whole process finishes.
Have a look at comments in following snippet:
Observable.create<List<PersonalizedFood>> {
FirebaseDTDatabase.getSavedDietFoodQuery(user.uid).addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError?) {
}
override fun onDataChange(p0: DataSnapshot?) {
val list = ArrayList<PersonalizedFood>()
p0?.let {
for (dateObject in p0.children) {
for (foodItem in dateObject.children) {
val food = foodItem.getValue(FBPersonalizedFood::class.java) as FBPersonalizedFood
list.add(PersonalizedFood(food))
}
}
}
it.onNext(list)
it.onCompleted()
}
})
}.subscribeOn(Schedulers.io()).flatMap {
Observable.from(it) // returning a Observable that emits items of list ("it" is the list here)
}.observeOn(Schedulers.io()).flatMap {
// How does this flatMap know that emission of all item has been finished so that onCompleted() method could be called.
personalizedFood ->
Observable.create<Boolean>{
FirebaseDTDatabase.getFoodListReference(personalizedFood.foodId).addListenerForSingleValueEvent(object :ValueEventListener{
override fun onCancelled(p0: DatabaseError?) {
it.onError(p0?.toException())
}
override fun onDataChange(p0: DataSnapshot?) {
if(p0 != null) {
val food = p0.getValue(FBFood::class.java)!!
val repo = LocalFoodRepository()
doAsync {
repo.insertFood(this#LoginActivity, Food(food.foodId, food.foodName, food.foodDesc))
repo.insertServingDetails(this#LoginActivity, food.servingList.map { it.component2() })
repo.saveFood(this#LoginActivity, personalizedFood)
it.onNext(true)
}
}else {
it.onNext(false)
}
}
})
}
}.observeOn(Schedulers.io()).doOnCompleted{
dismissProgressDialog()
finish()
}.doOnError{
it.printStackTrace()
dismissProgressDialog()
finish()
}.subscribe()
Thanks.
The Observable from the flatMap knows "when to all of the items have been finished" when all of the observables emitted by it have called onCompleted(). The second flatMap in your code never calls onCompleted() because none of the observables it creates call onCompleted().
You should call onCompleted() in your onDataChange() method. Since each of the observables created in the flatMap only emit one item, it can be called directly after the onNext() method:
override fun onDataChange(p0: DataSnapshot?) {
if(p0 != null) {
val food = p0.getValue(FBFood::class.java)!!
val repo = LocalFoodRepository()
doAsync {
repo.insertFood(this#LoginActivity, Food(food.foodId, food.foodName, food.foodDesc))
repo.insertServingDetails(this#LoginActivity, food.servingList.map { it.component2() })
repo.saveFood(this#LoginActivity, personalizedFood)
it.onNext(true)
it.onCompleted()
}
} else {
it.onNext(false)
it.onCompleted()
}
}

Categories

Resources