Below Code
why onnext called only once? When I remove subscribeOn it was called for every number.
when I subscribeOn io thread just once called (for 8658)
can someone explain it to me?
val subject = BehaviorSubject.create<Int>()
subject.onNext(2121)
subject.distinctUntilChanged().doOnNext {
Log.d("AHMET VEFA SARUHAN", it.toString())
}.subscribeOn(Schedulers.io()).subscribe(object : Observer<Int> {
override fun onSubscribe(d: Disposable?) {
}
override fun onNext(t: Int?) {
}
override fun onError(e: Throwable?) {
}
override fun onComplete() {
}
})
subject.onNext(5436)
subject.onNext(8658)
By using subscribeOn, the chain to observe the BehaviorSubject is established concurrently with the thread calling the onNexts. It takes time for a subscribeOn to take effect thus the main thread simply runs ahead and overwrites the subject's current value to the latest in the meantime.
There is no practical reason to use subscribeOn on a Subject in general.
Related
I am trying to save the data on the server to the local database with Room. Since these tables are related to each other, I want the insertion to be done in order.I listen to these operations with RxJava. For example i have school's and season's tables and and that's how I add the data:
fun insertAllSchools(vararg schools: School):Completable=dao.insertAll(*schools)
fun insertAllSeasons(vararg seasons: Season):Completable=dao.insertAll(*seasons)
When I create a separate method for each table, the insertion process is done, but I have to write a disposable method for each of them. Like this:
fun insertAllSchools(allData:ResponseAll){
if(allData.schoolList!=null){
disposable.add(
repositorySchool.insertAll(*allData.schoolList.toTypedArray())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableCompletableObserver(){
override fun onComplete() {
Log.d(TAG,"OnComplete")
}
override fun onError(e: Throwable) {
Log.e(TAG,"Error"+e.localizedMessage)
}
})
)
}
}
When one is complete, I call the other method, but this time there is a lot of unnecessary code.
I have tried different methods to combine these completable methods and work sequentially, but it does not add to the database even though it appears in the logs.
For example, I tried to combine it this way:
if(allData.schoolList!=null){
mObservable = Observable.fromArray(
repositorySchool.clearAllData(),
repositorySchool.insertAll(*allData.schoolList.toTypedArray())
)
disposable.add(
mObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableObserver<Completable>() {
override fun onComplete() {
Log.d(TAG,"onComplete")
isDataLoad.value = true
}
override fun onNext(t: Completable) {
Log.d(TAG,"onNext"+t)
}
override fun onError(e: Throwable) {
Log.e(TAG,"onError")
}
})
)
}
I do not receive any errors. How can I combine these completable methods and make them work sequentially. Thanks!
Edit(Solution): It works like this:------------>
if(allData.schoolList!=null) {
disposable.add(
repositorySchool.clearAllData()
.andThen(Completable.defer { repositorySchool.insertAll(*allData.schoolList.toTypedArray()) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableCompletableObserver() {
override fun onComplete() {
isDataLoad.value = true
}
override fun onError(e: Throwable) {
Log.e(TAG,""+e.localizedMessage)
}
})
)
}
I disagree with using doOnComplete(). In that case your not combining the Completables into a single Completable event that you can observe. What you probably want is something like doThingA().andThen(Completable.defer(() -> doThingB()) as mentioned in this answer on a similar question.
There is a method called doOnComplete() which you can use to make your second call through a lambda or jack.
I am trying to save data in Room and it requires some background thread to save data. So I have created an observable like this
val obs: Observable<MutableLiveData<List<Source>>>? = Observable.fromCallable(object :Callable<MutableLiveData<List<Source>>>{
override fun call(): MutableLiveData<List<Source>> {
return mutableLiveData
}
})
Then I am subscribing, observing and unsubscribing it like this
obs?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.unsubscribeOn(Schedulers.io())
?.subscribe(object : Observer<MutableLiveData<List<Source>>>{
override fun onComplete() {
}
override fun onSubscribe(d: Disposable?) {
}
override fun onNext(value: MutableLiveData<List<Source>>?) {
for(source in value!!.value!!.iterator()){
sourceDao.insert(source)//this is the line number 87, that logcat is pointing
}
}
override fun onError(e: Throwable?) {
e?.printStackTrace()
}
})
I am subscribing it on the Schedulers.io thread then observing it on the AndroidSchedulers.mainThread() but still I am getting not on background thread error. More specifically
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:204)
at android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:251)
06-18 11:11:08.674 3732-3732/com.theanilpaudel.technewspro W/System.err: at com.package.myapp.room.SourceDao_Impl.insert(SourceDao_Impl.java:63)
at com.package.myapp.main.MainRepository$saveToRoom$1.onNext(MainRepository.kt:87)
at com.package.myapp.main.MainRepository$saveToRoom$1.onNext(MainRepository.kt:76)
Execute your DB operation within the Observable and not in your Observer:
val obs: Observable<MutableLiveData<List<Source>>>? = Observable.fromCallable(object :Callable<MutableLiveData<List<Source>>>{
override fun call(): MutableLiveData<List<Source>> {
for(source in mutableLiveData!!.value!!.iterator()){
sourceDao.insert(source)
}
return mutableLiveData
})
.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.unsubscribeOn(Schedulers.io())
?.subscribe(object : Observer<MutableLiveData<List<Source>>>{
override fun onComplete() {
}
override fun onSubscribe(d: Disposable?) {
}
override fun onNext(value: MutableLiveData<List<Source>>?) {
// Nothing todo right more
}
override fun onError(e: Throwable?) {
e?.printStackTrace()
}
})
})
It makes sense because you have observeOn the main thread and you are doing your work in the observer (observeOn manages the thread of the observer).
To fix this, you can use flatmap on your obs and do the for loop in there. Since flatmap requires you to return an Observable, you can return Observable.just(true) after your for loop.
I'm currently trying to implement RxLifeCycle into my networking with RxJava. I've been using a subclass of Consumer, but for RxLifeCycle, you need to handle onError. So I have moved over to Observer.
The problem with this is that when the call is disposed, it's calling onComplete instead of onError, which I would prefer.
buildle.gradle:
// RxJava
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.0.3'
compile 'com.trello.rxlifecycle2:rxlifecycle-kotlin:2.2.1'
compile 'com.trello.rxlifecycle2:rxlifecycle-android-lifecycle-kotlin:2.2.1'
My previous NetworkConsumer was structured like this, and I would handle all the results in accept.
NetworkConsumer:
abstract class NetworkConsumer<T> : Consumer<NetworkResponse<T>> {
#Throws(Exception::class)
override fun accept(response: NetworkResponse<T>) {
...
}
// to override
open fun onSuccess(response: T) {}
open fun onComplete() {}
}
My network calls are all structured the same way using Single.
fun getFavorites(): Single<NetworkResponse<Array<MyObject>>>
And I'm using it like this.
service.getFavorites(...)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : NetworkConsumer<Array<MyObject>>() {
override fun onSuccess(response: Array<MyObject>) {
// use response
}
override fun onComplete() {
// do whatever, like hiding the loading view.
loading_view.visibility = View.GONE
}
})
I really like this setup as it allows me to move a lot of the logic from the calling Activity into the NetworkConsumer and only worry about handling the result.
However, with RxLifeCycle, you need to use an Observable instead of a Single. So I created a NetworkObserver to handle this change.
NetworkObserver:
abstract class NetworkObserver<T> : Observer<NetworkResponse<T>> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(response: NetworkResponse<T>) {}
override fun onError(e: Throwable) {}
override fun onComplete() {}
// other functions from NetworkConsumer
}
However, the problem is that onComplete is being called when the network call is disposed, which I would prefer to handle any UI changes in onComplete instead.
For example, I'm showing a loading screen when the network call is started, and I want to hide that loading screen when it's done, regardless if it failed or didn't.
I believe I just need to use a different Class instead of Observer for this, but I'm unsure which Class would work best for this.
The correct answer is SingleObserver, this is perfect for networking.
abstract class NetworkObserver<T> : SingleObserver<NetworkResponse<T>> {
override fun onSubscribe(d: Disposable) {
...
}
override fun onSuccess(response: NetworkResponse<T>) {
... handle onSuccess
}
override fun onError(e: Throwable) {
... cancelled or an error
}
}
Background
Some articles claim that Rx can replace AsyncTask and AsyncTaskLoader.
Seeing that Rx usually makes code shorter, I tried to dive into various samples and ideas of how it works.
The problem
All of the samples and articles I've found, including Github repos, don't seem to really replace AsyncTask and AsyncTaskLoader well:
Can't find how to cancel a task or multiple ones (including interrupting a thread). This is especially important for AsyncTask, which is usually used for RecyclerView and ListView. This can also be done on AsyncTaskLoader, but requires a bit more work than just call "cancel". For AsyncTask, it also provides publishing of a progress, and that's another thing I can't find an example of in Rx.
Can't find how to avoid unsubscribing manually while avoiding memory leaks, which quite ruins the whole point of using Rx, as it's supposed to be shorter. In some samples, there are even more callbacks than normal code has.
What I've tried
Here are some links I've read about Rx:
https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/ - this actually provides a cool way to handle AsyncTaskLoader, as it caches the result. However, the samples don't work at all (including building them), as they are very old.
https://code.tutsplus.com/tutorials/getting-started-with-rxjava-20-for-android--cms-28345 , https://code.tutsplus.com/tutorials/reactive-programming-operators-in-rxjava-20--cms-28396 , https://code.tutsplus.com/tutorials/rxjava-for-android-apps-introducing-rxbinding-and-rxlifecycle--cms-28565 - all those talk mostly about RxJava, and not about replacement of AsyncTask or AsyncTaskLoader.
https://github.com/L4Digital/RxLoader - requires unsubscribing, even though it doesn't say it needs it.
The question
How can I use Rx as replacement for AsyncTask and AsyncTaskLoader?
For example, how would I replace this tiny AsyncTaskLoader sample code with Rx equivalent:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportLoaderManager.initLoader(1, Bundle.EMPTY, object : LoaderCallbacks<Int> {
override fun onCreateLoader(id: Int, args: Bundle): Loader<Int> {
Log.d("AppLog", "onCreateLoader")
return MyLoader(this#MainActivity)
}
override fun onLoadFinished(loader: Loader<Int>, data: Int?) {
Log.d("AppLog", "done:" + data!!)
}
override fun onLoaderReset(loader: Loader<Int>) {}
})
}
private class MyLoader(context: Context) : AsyncTaskLoader<Int>(context) {
override fun onStartLoading() {
super.onStartLoading()
forceLoad()
}
override fun loadInBackground(): Int {
Log.d("AppLog", "loadInBackground")
try {
Thread.sleep(10000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
return 123
}
}
}
For AsyncTask, usually we set it per ViewHolder, as a field there, and we cancel it upon onBindViewHolder, and when activity/fragment gets destroyed (go over all those that still pending or running).
Something like this (sample made as short as possible, of course it should be changed depending on needs) :
class AsyncTaskActivity : AppCompatActivity() {
internal val mTasks = HashSet<AsyncTask<Void, Int, Void>>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_async_task)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = object : Adapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(this#AsyncTaskActivity).inflate(android.R.layout.simple_list_item_1, parent, false))
}
#SuppressLint("StaticFieldLeak")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.tv.text = "loading..."
if (holder.task != null) {
holder.task!!.cancel(true)
mTasks.remove(holder.task!!)
}
holder.task = object : AsyncTask<Void, Int, Void>() {
override fun doInBackground(vararg voids: Void): Void? {
for (i in 0..100)
try {
Thread.sleep(5)
publishProgress(i)
} catch (e: InterruptedException) {
e.printStackTrace()
}
return null
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
holder.tv.text = "progress:" + values[0]
}
override fun onPostExecute(aVoid: Void?) {
super.onPostExecute(aVoid)
mTasks.remove(holder.task)
holder.tv.text = "done:" + position
holder.task = null
}
}.execute()
}
override fun getItemCount(): Int {
return 1000
}
}
}
override fun onDestroy() {
super.onDestroy()
for (task in mTasks)
task.cancel(true)
}
private class MyViewHolder(itemView: View) : ViewHolder(itemView) {
internal var tv: TextView
internal var task: AsyncTask<Void, Int, Void>? = null
init {
tv = itemView.findViewById(android.R.id.text1)
}
}
}
Let me tell you in advance that my experience in RxJava isn't as some would call it advance. I just know enough to replace AsyncTask using RxJava + RxAndroid. For example, let's convert your AsyncTask code to RxJava2 equivalent using Observable, which is arguably the most popular stream type.
Observable
.create <Int> { emitter ->
try {
for (i in 0..100) {
Thread.sleep(5)
emitter.onNext(i)
}
} catch (e: InterruptedException) {
emitter.onError(e)
}
emitter.onComplete()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ i ->
holder.tv.text = "progress:" + i
}, { e ->
e.printStackTrace()
}, {
holder.tv.text = "done:" + position
})
Now let's go through the code line-by-line:
create lets you customize emitter behavior. Note that this isn't the only method to create the streams, there are also just, fromArray, fromList, defer, etc. We can discuss more of that if you are still interested.
subscribeOn(Schedulers.io()) tells RxJava that #1 will be executed in I/O thread. If this is a background operation, Schedulers.newThread() also works (at least to my knowledge it does).
observeOn(AndroidSchedulers.mainThread()) tells RxJava that #4 will be executed in Android's main thread. This is also where RxAndroid fills the gap since RxJava doesn't have any reference of Android.
subscribe()
onNext(i : Int) - equivalent to publishProgress(). In newer RxJava, emitted value cannot be null.
onError(e : Throwable) - error handling that is long glorified by RxJava's programmers. In newer RxJava, onError() must be provided, doesn't matter if it's empty.
onComplete() - equivalent to onPostExecute().
Again, there might be a lot of misinformation with my answer. I expect someone with better understanding to correct it or provide better answer that this one.
It seems there is no way to implement AsyncTask functionality with RxJava in shorter or more simple form. With RxJava you simplify task chaining, so if you have only one task AsyncTask can be better solution, but when you have several consecutive tasks to do - RxJava's interface is more convenient. So instead of, for example, program onCancelled for each task you should rather implement some common logic.
LiveData observer does not trigger when a callback is emitted. But if I put the observable data in a function and change the data it works just fine. Please any ideas what would it be?
fun connect(address: String){
protocolARC.value = ProtocolARC() //OKAY!!!!
bleManager.gattClient.onCreate(getApplication(), address, object : GattClient.OnCounterReadListener {
override fun onCounterRead(value: Int) {
Log.d("HURRAY", "read")
protocolARC.value = ProtocolARC() //NOT OKAY?????
}
override fun onConnected(success: Boolean) {
stopScan()
}
})
}
The problem was in a callback. It was not being triggered from the main thread.