https://kotlinlang.org logo
#orbit-mvi
Title
# orbit-mvi
s

Stephan Schuster

10/12/2021, 9:48 AM
Hi again, as part of some architecture documentation I created the attached diagram. Does this make sense? Is it correct in terms of Orbit?
❤️ 1
👏 1
🙏 1
I also have a detailed textual description for it, but I decided to omit it here for now.
m

Mikolaj Leszczynski

10/12/2021, 10:23 AM
yeah, this looks correct! A bit puzzled by
suspend fun Flow<X>
being returned from your data layers. Could you satisfy my curiosity? 🙂
s

Stephan Schuster

10/12/2021, 10:54 AM
Oh, I guess this is a misunderstanding. The thin downward arrows to the left are
fun
for VM and
suspend fun
for the data layers. The
Flow<X>
are meant to be the thick upward lines/streams on the right. I took this visualization more or less from the android docs regarding flow. Of course, there is no
suspend fun Flow<X>
.
m

Mikolaj Leszczynski

10/12/2021, 10:57 AM
so this is just an example that you could have either
suspend fun
or
Flow<X>
, yes?
s

Stephan Schuster

10/12/2021, 11:10 AM
Or both, yes. But they are in any case separate things.
👍 2
m

miqbaldc

10/12/2021, 11:31 AM
awesome thanks a lot, with this diagram, made life easier to onboard new member 😉
m

Mikolaj Leszczynski

10/12/2021, 11:34 AM
This would make for a nice article that shows how orbit fits in app architecture at large 😉
❤️ 1
s

Stephan Schuster

10/12/2021, 12:18 PM
Glad to hear, that the diagram is obviously correct and even helpful for some of you. Easier onboarding of new members is exactly the reason why I created it. Regarding the article, I then could/should actually publish the whole architecture documentation I just wrote. But I doubt that this is in line with my company's policy. To be honest, I already had doubts sharing this diagram. Nevertheless, I will make up my mind regarding such an article. And BTW: Now that I somehow published the diagram already, you could also use it (in a modified version) for your docs - if you think it helps and makes sense.
🔝 1
1
🙏 1
m

Mikolaj Leszczynski

10/12/2021, 12:20 PM
Absolutely no pressure @Stephan Schuster! Thanks for the effort, I think it could be a nice diagram to have in the docs as the threading documentation is a bit thin at the moment :)
s

Stephan Schuster

10/12/2021, 12:23 PM
Yes, absolutely agree. At least for me it would have helped there in the beginning. Feel free to reuse it in your docs.
👍 2
Minor improvement on visualization in order to avoid such misunderstandings as above.
❤️ 1
K 1
🎉 1
🙏 1
m

Mikolaj Leszczynski

10/12/2021, 1:15 PM
Nice, thanks 🙂 Obvs I won’t use the image as is anyway since it’s proprietary IP, we have to come up with our own. But I’ll definitely use this as an inspiration
👍 2
s

Sam

10/30/2021, 8:41 PM
@Stephan Schuster in terms of Dispatchers: At the second row, Why should use
Default
= Dispatchers.Unconfine and
Usually
= Dispatchers.Default ? I think we can use only 2 kind of dispatchers: 1. Dispatchers.Main for UI 2. Dispatchers.IO for API, database..etc Thanks/
1
s

Stephan Schuster

11/01/2021, 8:49 AM
@Sam I was trying to indicate the following: Orbit's "event loop" is by default running on `Dispatchers.Default`: see Container.kt#L87 For each intent in the "event queue", Orbit launches a coroutine with `Dispatchers.Unconfined`: see RealContainer.kt#L92 As a consequence, without doing anything, your intents will be executed on
Dispatchers.Default
. If you want to switch context to
<http://Dispatchers.IO|Dispatchers.IO>
, you have to do this manually or rely on the implicit behavior of e.g. Room or Retrofit which both do this for you automatically. Does that make sense?
🙏 1
@appmattus, @Mikolaj Leszczynski: For a scenario which starts with a button click on the UI and ends with Room DB call, this entails 2 thread jumps, right? Main -> Default -> IO Assuming that you also want to display the result of the Room suspend function on your UI, it would be 4 thread jumps, right? Main -> Default -> IO -> Default -> Main
m

Mikolaj Leszczynski

11/01/2021, 9:06 AM
That’s correct @Stephan Schuster
👍 1
s

Sam

11/01/2021, 9:44 AM
@Stephan Schuster Im a new comer in Orbit-mvi and MVI I have a several scenario to double-check with your diagram In terms of Thread/Dispatcher Cases 1: In chat app with Socket IO
Sender
Step 1: User A send a message “Hello worlds” Step 2: Activity will call a viewmodel to send a message -> it will go through these threads -> Main (click a button) -> IO ( call API ) -> Default (Send a state Success/Error) -> Main (Show a alert to UI )
Receiver
Step 1: Socket IO will listen and receive a message -> we have to setup Socket IO in Activity Step 2: Socket IO will receive a message ( Im not sure which thread it will come from - Ref https://socket.io/blog/native-socket-io-and-android/ ) Step 3: ViewModel will receive a message from SocketIO -> it will go through these threads ->
another thread from Android UI thread
to receiving a message-> IO ( save Room ) -> Default ( Send a state Success/Error ) -> Main ( add to recyclerview) is it correct ways?
s

Stephan Schuster

11/01/2021, 1:17 PM
@Sam First off, with my diagram I just wanted to visualize the default and probably 80% use case. That said, you can have of course different threading scenarios and also adjust Orbit if really needed. But this is probably more for advanced usage scenarios. While it is always a good idea to think about what is happening under the hood, Orbit, Room, Retrofit etc. come with sensible defaults that can be used right out of the box without digging too deep into the threading details of each framework. Regarding your sample, I was not able to completely follow along. Overall it looks overly complex to me. But I am also no expert. From high-level perspective I would give the following advice(s): • Divide your app into architectural layers "ui" (Jetpack Compose or Android UI Toolkit), "viewmodel" (Jetpack ViewModel + Orbit), "repository" (abstraction of data sources + data mapping) and "data source(s)" (e.g. Room, Retrofit, your API via SocketIO, ...) --> see diagram. If you have enough domain logic, you could also add "usecases/interactors" between "viewmodel" and "repository". • Your chat API is a data source that is accessible via Socket IO. It provides the chat state and functions to modify the latter. It should typically act on Dispatchers.IO. So you will end up in the same threading model indicated by my diagram. • Expose the state of your chat app (the messages etc.) as a Flow (probably using callbackFlow) --> see diagram. • Expose suspend functions to modify the chat state (e.g. send/add new message) --> see diagram. • Also expose appropriate flows and (suspend) functions in your repository (along with data mapping) and view model. • Collect/observe the state flow in your UI to display/update the chat messages --> see diagram. • To send messages (and modify the state) you want to invoke Orbit intents in your VM which delegate to suspend functions in your repo which in turn delegate to your chat data source --> see diagram. If you want to cache your chat messages locally (you probably should), add a second data source based on Room. Your repository will mainly interact with the local Room data source then but use your API/socket IO data source to update Room when internet access is available. For all upper layers it should be transparent if you use one or two data sources. The Official Orbit Stock List samples should also be helpful to you. They do similar things with a slightly different architecture layering but same principle: streaming API mapped to Flow and then pass it through upper layers.
Update and example
❤️ 3
4 Views