- Edited
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
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.
In Rx, you have many ways to fix this, like using Interval, Debounce or Buffer operators. Let's try the Buffer operator
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-swift-nyc-2017-krunoslav-zaher-modern-rxswift-architectures/
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
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-swift-nyc-2017-krunoslav-zaher-modern-rxswift-architectures/
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