LebGeeks

A community for technology geeks in Lebanon.

You are not logged in.

#1 October 13 2018

vlatkozelka
Member

ReactiveX

I've been working in Android development for 2 years now, and a common issue I found is that whenever a project starts getting bigger, things get complicated, unmaintainable, crashes everywhere, and everything starts to take a lot more time to develop than it would initially seem like it should.

We never used any pattern on top of the Android framework, like MVC, MVP, etc... because quite honestly, my team leader and I never believed in these patterns, especially on Android's vicious lifecycle system. But that lead to the issues I just mentioned above.

Recently, we moved from using Java with classic Android development ways, to using Kotlin + RxKotlin + RxFeedback. And we are loving it!

I will go a bit into details, to explain why moving from classic OOP programming without patterns, to functional and reactive programming with Rx and Kotlin worked so well for us.

Rx

Rx stands for ReactiveX or Reactive Extensions, which is a series of libraries, one for each language , like Js, Java, kotlin ,etc... that implement the Observer pattern along with functional and reactive programming.

Observer patterns means that you would stop relying on listeners and broadcasts everywhere, and start working with Observables, which are mainly streams of events that can carry data.

Some pros about using Rx:

- Thread safety

Thread safety is a huge issue in mobile development. The users nowadays can't wait at all for a page to load in a mobile app. Using dialogs to block them while something loads is becoming a crime. So any lengthy operations have to go to the background, which creates more and more complications for developers. The nice thing about Rx is that switching between threads is very easy, with the use of Schedulers.

Example:

Using Android's Loopers

Handler mainThreadHandler = new Handler();
        //created a handler on the mainthread, which would have already called Looper.prepare(), so we can post to this.

        //now to run our background operation
        Thread backgroundThread = new Thread() {
            @Override
            public void run(){
                //do some lengthy operation, like a network call or a big file read
                DataObject data = doLengthyDataQuery();
                //post the result back to the mainThread
                mainThreadHandler.post(new Runnable() {

                    //update the UI with the queried data
                    updateUI(data);
                });
            }
        }.start();

Using RxKotlin

    Observable.create<DataObject>() { emitter ->
            val data = doLengthyDataQuery()
            emitter.onNext(data)
        }
                .subscribeOn(Schedulers.io())//the function above will run on a background thread called io
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { data -> updateUI(data) }

- Disposables: In the first example, it shows how clean it is to write multi-threaded code, but it doesn't show also how safe it is.
So now let's take the scenario where the user leaves the screen (Activity) while the thread is doing the background operation. The app will most likely crash, because I called updateUI(), when the UI itself no longer exists. This can be mitigated by checking first, but imagine having to do so every time you need to update UI, just to be sure.

In Rx, you can just dispose of the subscription when you no longer need it.

class TestActivity : AppCompatActivity() {

    val disposables = mutableListOf<Disposable>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
        val subscription = Observable.create<DataObject>() { emitter ->
            val data = doLengthyDataQuery()
            emitter.onNext(data)
        }
                .subscribeOn(Schedulers.io())//the function above will run on a background thread called io
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { data -> updateUI(data) }

        disposables.add(subscription)//keep track of the subscription to dispose it later
    }

    override fun onPause() {
        super.onPause()
        //disposing the subscriptions so the app ignores the events when it no longer needs them
        disposables
                .filter { !it.isDisposed }
                .forEach { it.dispose() }
    }


}

- Rx operators: Rx comes with a great number of useful operators, that make your life easier. Let's consider a stream of events, that is too fast for your UI to handle, and that would freeze the UI, because it's too "spammy". Fixing the issue in Java would mean that you have to store the time of each event somewhere, and only handling the event if enough time has elapsed. Which is boilerplate code that a lot have written many times.

In Rx, you have many ways to fix this, like using Interval, Debounce or Buffer operators. Let's try the Buffer operator

 Observable.create<ScanResult>() { emitter ->
            val bluetoothManager = BluetoothManager.getInstance()//fake methods 
                    .scan()// scan would send a result everytime anything changes about the bluetooth adapter, it's very spammy!                    
        }
                .subscribeOn(Schedulers.io())//the function above will run on a background thread called io
                .observeOn(AndroidSchedulers.mainThread())
                .buffer(1L,TimeUnit.SECONDS)//only emit results every second
                .subscribe { scanResults -> updateUI(scanResults) }

I just fixed the issue in 1 line!

I can go on about the pros of Rx, but I don't want this post to end up too lengthy.
You can watch this video, from Jake Warthon, the top contributor to AOSP, about why we need Rx. Only watch the introduction though, the rest is very theoretical. https://www.youtube.com/watch?v=0IKHxjkgop4


Some cons of using Rx:

- Steep learning curve: There's a lot of operators to learn, and it's a new way of thinking, so it's not something you can learn in a day, and it needs a bit of practice. Though it's very rewarding to try and learn.

- Debugging Rx errors: Don't get this one wrong, debugging using Rx is actually easier, because you can add logs in a very structured fashion. What I'm talking about is Rx errors themselves, they have a huge stacktrace, they usually leave you in a sort of trauma, and they usually are due to some very minor error. You just need to learn how to read through them.


Now that's enough about Rx itself I guess, please watch the video if I caught your interest, it is a very good example to why we need this sort of programming in our lives.



RxFeedback

So Rx in itself is not a pattern for your app, it is more of a tool and a way of thinking maybe. But it still doesn't streamline the development process, and very often you will find yourself (or co-workers) doing the same thing in a 1000 different way. So you need a pattern, kind of like what MVP is, but that works well for modern apps. This is where RxFeedback comes in.

RxFeedback mainly consists of these 4 elements:

- State: The state of what you're developing. It could be your whole app's state encoded in there if it's not so big, or the state of different modules and features.
- Events: They are the things that will lead to changing your state. Nothing can change the state unless there is an event behind it.
- Reducer: A very simple concept, it is what takes the current state, and an incoming event, and reduces these to a new state. Basically, old state + event = new state
- Feedback: This is where RxFeedback differs from other similar concepts like Redux. Sometimes, the event doesn't come from an external source, like a button click, but as a reaction to the state itself. This is why it's called a feedback, it's your own state inducing an event on itself.

Let's take a real life scenario, out of the development and technical world, to make things easier.
So let's say I'm building a state machine about myself.
- My initial state is the healthy me, walking around, etc...
- An external events comes in, I get hit by a rock (maybe from a lebgeek due to recent posts )
- So healthy me + event(getting hit by a rock) = me bleeding

But the state machine doesn't stop there. I have to go fix my bleeding, no? So a reaction to my own state, is I go to the doctor. That's an event that didn't come from an external source, but from within the state machine itself.
- me bleeding -> event (go to the doctor)
- me bleeding + go to the doctor = problem fixed = a new state once again

The beauty of this system is that you can most likely code anything with it. I know robotics have been doing this since forever, and it worked so well for us on Android.

Also, like I said, it streamlines the development process. Because your app will follow a pattern. State, reducer, events and feedbacks. Your UI will no longer have any code in it, it just attaches to the state, and render from that. The reducer is the only thing that is allowed to change the state, which leads to easier maintenance and debugging. When there's a bug, it's in the reducer 99% of the time.

Unfortunately, I cannot share any code I wrote because those are proprietary, and writing an example would be a very long task. If anyone is interested, please comment and maybe I'll follow up with an example. But you can for now go check out the library itself.
https://github.com/NoTests/RxFeedback.kt
https://github.com/NoTests/RxFeedback.swift
https://github.com/NoTests/RxFeedback.js

Just choose the one that suits the technology you are working with. And make sure to watch the video if you find this interesting.
https://academy.realm.io/posts/try-swif … itectures/

This guy, and Jake Warthon both got hired at Google to push this kind of development.
Uber also refactored all of their app to use something built on the same idea, called Uber-RIBs
So I am sure it will become trendy (or some other version of it) really soon.

Thanks for reading, and feel free to comment if I said anything wrong, so I could edit the post.

Update: I added a better example in the comments

Last edited by vlatkozelka (May 28 2019)

Offline

#2 October 14 2018

rolf
Member

Re: ReactiveX

Thanks for your post. Separation of concerns is important. I used to put all my code in one file. It is OK when the file is 500 lines and you know the code like the back of your hand. But eventually it becomes too much work to find where something takes place and changing the code becomes risky due to side effects.

React helps with this problem, so does any pattern that tells you what should be where.

Offline

#3 October 14 2018

vlatkozelka
Member

Re: ReactiveX

rolf wrote:

I used to put all my code in one file.

First company I worked with worked like this. PHPHTMLCSSJS files ... was a nightmare! Especially when your boss comes in like " Just lower this part of the page a bit", and that "part" has php, css and JS code in it.

rolf wrote:

React helps with this problem, so does any pattern that tells you what should be where.

Yes but the problems with patterns out there is that you end up violating that rule.

For example, an engineer at Uber, and one from Square, both gave speeches, where even when they separated concerns using MVVM, they ended up with controllers that have to know about others. It's because in their app, the user can change his mind about anything at any point. For example, you can change the car type right before checkout. So that change has to propagate all the way back to the first controller that spawned this whole flow.

A bit lengthy, but really interesting videos. Uber even open sourced their research on this, with plugins for the IDE to auto-generate the classes!

https://www.youtube.com/watch?v=KjoMnsc2lPo
https://www.youtube.com/watch?v=yeFgxLWRz6c&t=1801s



With RxFeedback/Redux, you aren't violating any constraints, you just push an event like UserChangedCarType for example. The reducer will be like:

oldState: { carType: cheap, totalAmount : 10$} + event :{change car type to expensive} = newState { carType: expensive, totalAmount: 20$}

And any UI attached to the state, will update to show the new payment.

What I'm trying to say, is that you end up separating concerns, in exception of the logic that updates the state ( the reducer ). And this idea seems to work well from my experience, because most of the time you can't really get away without something that keeps all of your app together.

Now this does mean that you end up with a huge State class and a huge Reducer class. But this is where you can start dividing a big state machine into smaller ones, that end up updating a "mother" state machine that works at a larger scope. Redux explains that well. RxFeedback is a bit newer so there's no concrete way of doing it yet. Though my team and I are figuring something out.

I'll try and write an example soon. All I'm writing makes no sense without a proper example after all. I'll try and see if I can grab the code from the workshop we once did.

Last edited by vlatkozelka (October 14 2018)

Offline

#4 October 14 2018

Adnan
Member

Re: ReactiveX

This ReactiveX thing looks very interesting. I use React + Redux and the single source of truth the Redux Store provides really comes in handy when handling complex user interactions (such as the car changing at anytime example you mentioned).

First time I saw ReactiveX is when it popped up on my Wappalyzer plugin on Chrome when browsing Facebook.

But what would be the added benefit of Rx over Redux? Can they be used simultaneously?

Offline

#5 October 14 2018

vlatkozelka
Member

Re: ReactiveX

Adnan wrote:

But what would be the added benefit of Rx over Redux? Can they be used simultaneously?

Redux is a pattern, whereas Rx is a tool, that replaces interfaces and listeners with event streams, in order to replace pulling data with pushing data to your UI, and help code in a reactive way, rather than in an imperative way.

This won't make sense without an example, so here we go. I'll use Kotlin cause it has sealed classes, which helps a lot.

So you already know Redux, it's quite simple as a concept. Now if you add Rx to it, you will have RxFeedback, which is basically a state machine, similar to redux, but that also monitors the state itself, to fire more events when needed.

So let's take Bluetooth Low Energy (I'll refer to this as BLE) scan as an example. For BLE scan to work an Android, you need all of these conditions, plus one that I will add as a requirement for our example app:

- Location permission (idk why Android needs location for BLE to work)
- Location needs to be ON
- Bluetooth permission
- Bluetooth needs to be ON
- Requirement from the app: the user needs to be on a certain screen, and not scan for devices all the time.


So here's the cool thing about this, I can basically encode the problem directly into a state class, like so:

data class AppState(
        
        var isLocationPermissionGranted: Boolean = false,
        var isLocationEnabled: Boolean = false,
        var isBluetoothPermissionGranted: Boolean = false,
        var isBluetoothEnabled: Boolean = false,
        var isScanEnabled: Boolean = false, //our app requirement to only scan on certain screens
        var scanResults: List<ScanResult> = listOf()
        
)

Where RxFeddback will help, is that the fields above will be updated Asynchronously, since you don't really have any control over what the user will do. He can grant permissions, then turn on hardware, or vise-versa. he can go to the scan screen before being ready to scan. The important thing is for the app to react correctly. Doing this without Rx and Redux would be a nightmare. Imagine having to manage all these async events through callbacks!

So now comes the fun part. We need to write a system, that monitors the hardware availability, the screen the user is on, and react accordingly. So we need to write events, and feedbacks.

Here are the events that will happen in the app

sealed class AppEvent{

	//using data class here is better for logging
	//it's good for whenever one of the events carries data

	//Bluetooth adapter events 

	data class BluetoothPermission(val granted: Boolean): AppEvent() 
	data class BluetoothStateChanged(val enabled: Boolean) :AppEvent()	

	//Location adapter events

	data class LocationPermission(val granted: Boolean): AppEvent()
	data class LocationStateChanged(val enabled: Boolean): AppEvent()
	
	//scan events 

	data class EnableOrDisableScan(val enable: Boolean) : AppEvent()	
	data class FoundDevices(scanResults: List<ScanResult>): AppEvent()

}

our Reducer is pretty straight forward also

object Reducer{

	//take an oldState, handle and event, and produce a newState as a result
	fun reduce(oldState: AppState, event: AppEvent): AppState{

	//logging here would be extremeluy helpful. This is what I meant in the original post that using 
	//this pattern makes logging very clear. Since any app event will pass by this code. you can 
	//easily monitor what is going on in the App, without even looking at the UI!

	Log.d("AppEvent","$event")


	val newState = when(event){ //this is like switch case, to handle each type of events

		//copy the oldState which will be our new state, after applying a certain mutation, in the incoming apply block
		is AppEvent.BluetoothPermission -> oldState.copy().apply{
			//simply mutate the state by grabbing the value from the event
			isBluetoothPermissionGranted = event.granted
		}
		is AppEvent.BluetoothStateChanged -> oldState.copy().apply{	
			isBluetoothEnabled = event.enabled
		}
		is AppEvent.LocationPermission -> oldState.copy().apply{
			isLocationPermissionGranted = event.granted
		}
		is AppEvent.LocationStateChanged -> oldState.copy().apply{
			isLocationEnabled = event.enabled
		}
		is AppEvent.EnableOrDisableScan -> oldState.copy().apply{
			isScanEnabled = event.enable
		}
		is AppEvent.FoundDevices -> oldState.copy().apply{
			scanResults = event.scanResults//this logic might be more complex, this is just an example.
		}

	}
}


}

now in order to enable/disable scan, we need to monitor the state. This is called querying, and so we write queries. Fortunately, swift and kotlin allow us to write extensions functions. So we can store them outside of the class file, but still call them via `this`

The queries need to return the data that the system needs to do some effect. For example, to start a scan, all of the above conditions need to be met, and we will only scan for 5 seconds, so the query will return 5

What I like to do is store queries in a file like AppStateQueries.kt

fun AppState.enableScan():Optional<Long>{ //I'll explain later why this needs to be an Optional
//inside this extention is as if I was inside of the AppState class
// so I can directly access AppState fields

return if(isLocationPermissionGranted
	&&isLocationEnabled
	&&isBluetoothPermissionGranted
	&&isBluetoothEnabled
	&&isScanEnabled){
	Optional.Some(5) //scan for 5 seconds
	}else{
	Optional.None() //do nothing
}

}

Ok now for the last part. The System that will tie all of these together. Using what's called a feedback loop.
A feedback is basically this type


typealias Feedback = (ObservableSchedulerContext<AppState>) -> io.reactivex.Observable<AppEvent>

It looks weird, I know, but that's the case with all functional programing at first.

(ObservableSchedulerContext<AppState>) -> io.reactivex.Observable<AppEvent> basically means, a function that transforms a stream of State into a stream of Events. Which is what we are trying to do. As soon as our state reaches a point where it can scan for BLE devices, it will fire the appropriate event.

Now to create the System, we need to provide 3 things:

- An initial state, a reducer, and list of feedbacks.

I'll now explain how to build the System, in a class called AppSystem

//class declaration

fun initSystem(): Observable<AppState>{

return Observables.System( //Observables is class provided in the RxFeedback library
	initialState = AppState(),	 // We declared AppState class with all default values, so () is enough here
	Reducer::reduce, // a pointer to the reduce7 function, to be used by the System
	//now for a list of feedbacks
	scanFeedback,
	externalEventsBindings // more on this next
	)
}



//now to declare feedbacks
// there are 2 ways to do this, either using the react<State,Any,Event>() class, to react to a certain state
// or by using the bind operator, to create a feedback using external events, like Bluetooth and location

//so lets first handle bluetooth and location, for that we need to monitor the hardware and permission
// I will write pseudo-code for this just to give and idea


//wrap a listener into an observable
private val bleStateObservable = Observable.Create<AppEvent>{ emitter -> 

	//assuming we have some helper to monitor BLE

	BleHelper.monitorBleState(object: OnBleStateChangedListener(){
		
		@override fun onBleAdapterStateChanged(enabled: Boolean){
			//push and event into the stream
			emitter.onNext(AppEvent.BluetoothStateChanged(enabled))
		}

		@override fun onBlePermissionsChanged(enabled: Boolean){
			//push and event into the stream
			emitter.onNext(AppEvent.BluetoothPermission(enabled))
		}

		})

}

//  same for Location

private val  locationStateObservable =  // ...

//bind the external event sources to the system

private fun externalEventsBindings() = bind<AppState, AppEvent> {
	Bindings(
		subscriptions = listOf(),
		events = listOf(
			bleStateObservable,
			locationStateObservable
			)
		)
}



//our last feedback reacts directly to our app state itself, not an external source
// it's like saying, because I have all of the conditions available, I should start a scan
//so in code, it looks like this

private val scanFeedback = reactc<AppState,Long,AppEvent>( //react to an appstate, query a long value, and fire an AppEvent as a result
	query{//query is what we monitor from the state. Here we query the readiness to scan, waiting for a Long value, which is the duration to scan for
		it.enableScan()//the query we wrote earlier
		},
		effects{ scanDuration: Long ->

			//now that we have the duration, we can scan

			 //assuming such function exists, and that it takes a listener to callack with the scan results. In Kotlin we can use Lambdas
			BleHelper.scan(scanDuration){ scanResults -> //listener shortened into a lambda
				AppEvent.FoundDevices(scanResults)
			}

		}
		)

So now, as soon as you call initSystem(), you will get a state observable, subscribe to that, and the system will run, and provide state updates to the UI.

For example, I can now update a screen with a list of devices, or show a warning that the BLE adapter isn't ready, simply by subscribing to the AppState stream, e.g the system we just created

//inside some activity  ...

private fun subscribeToAppState(): List<Disposable>{
	val disposables = mutableListOf<Disposable>()
	val appStateObservable = AppSystem.appStateObservable // provided I stored it in a static field

	//now we update UI based on state pushes

	//show a list of devices
	disposables.add(
		appStateObservable.map{ it.scanResults }.subscribe{ updateSomeListAdapter(it) }
		)

	//show warning
	disposables.add(
		appStateObservable.map{it.isLocationPermissionGranted}.subscribe{ 
			/*show a warning if not granted, it is best to not compute anything here, and put such code in a ViewModel 
			 as to keep your UI code as concise and maintainable as possible
			*/
			 })

	//etc ...

	return disposables //don't forget to dispose these at activity pause to prevent UI crashes
}

I hope this covers up your question, and I hope the example is as clear as I think it is in my head. I tried to explain it all as fast as I can.
It may seem like overhead at first, but trust me, we couldn't get BLE to work before this. The state, conditions and requirements I demoed are like 10th of what our app needs to do, in terms of BLE alone, now add Camera, wifi, user clicks, etc... And you know it would be a mess without this sort of organization.

Plus, there are perks of using this structure, which are that anyone who takes your code-base and wants to continue on it, can easily open just the event and state classes, and understand rapidly what needs to be done. Then to debug, all he has to do is debug a small part of the reducer that is failing...


Bonus:


- Why use Optional<*>?


The Feedback system works by querying your state each time it changes, in order to fire events any time it needs to. Once it does, the system subscribes to the Observable<Event> that is created, and keeps the subscription as long as the value remains the same.
This is perfect for when a user spams the scan button for example. Even if 10000 scan events fly, the value of the query will remain the same, and your system will only create one Observable and subscribe to that, so the spam the user did will have no effect.

Whenever the query returns an Optional.None, the system unsubscribes from that stream.

It is basically a way to say: yes I want the event to happen/ No, I do not want the event to happen. Using Optionals

- Some things that need care

The system observable needs at least one subscription to stay "alive" and working. It also needs to be shared and replayed, so any UI that attaches to it any point can read from it the last value. I can't share that code, I hope you understand, but if you ever try this out, you'll know what I'm talking about and should be able to fix it.

You can bind to a events subject in order to push events from the UI into the system, using the bind operator I demoed to wrap BLE state events.

Last edited by vlatkozelka (October 14 2018)

Offline

Board footer