项目作者: gfranks

项目描述 :
Kotlin Flow Based Reactive Architecture
高级语言: Kotlin
项目地址: git://github.com/gfranks/KFlow.git
创建时间: 2021-01-29T18:53:12Z
项目社区:https://github.com/gfranks/KFlow

开源协议:MIT License

下载


KFlow

Kotlin Flow Based Reactive Architecture.

Install

Gradle/JitPack

  • Add JitPack to your top-level build.gradle file
    1. allprojects {
    2. repositories {
    3. ...
    4. maven { url "https://jitpack.io" }
    5. }
    6. }
  • Add KFlow to your module’s build.gradle file
    1. dependencies {
    2. implementation "com.github.gfranks:KFlow:0.0.2"`
    3. }

Usage

Android View Model

Start by extending either KFlowViewModel (a.k.a ViewModel) or KFlowAndroidViewModel (a.k.a AndroidViewModel). You’ll need to provide an implementation for abstract functions emitter(): Emitter and bind(flow:): Flow. Here’s an example of a basic view model with only a single action, in this case HelloWorldAction.Load:

  1. sealed class HelloWorldState {
  2. data class Initial(val helloWorld: HelloWorld? = null): HelloWorldState()
  3. object Loading: HelloWorldState()
  4. data class LoadSuccess(val page: Int, val nextPage: Int?, val value: HelloWorld?): HelloWorldState()
  5. data class LoadFailed(val error: String? = null): HelloWorldState()
  6. }
  7. sealed class HelloWorldAction {
  8. data class Load(val user: User, val page: Int = 1): HelloWorldAction()
  9. }
  10. private const val LinkKey = "Link"
  11. @FlowPreview
  12. class HelloWorldViewModel(application: Application): KFlowAndroidViewModel<HelloWorldAction, Response<HelloWorld>, HelloWorldState>(application) {
  13. @Inject
  14. lateinit var repository: HelloWorldRepository
  15. override fun emitter() = object : Emitter<HelloWorldAction, Response<HelloWorld>, HelloWorldState> {
  16. override val initialState: HelloWorldState
  17. get() = HelloWorldState.Initial()
  18. override suspend fun perform(action: HelloWorldAction): Flow<Output<HelloWorldAction, Response<HelloWorld>>> =
  19. flow {
  20. when (action) {
  21. is HelloWorldAction.Load -> emit(Output(action, repository.loadHelloWorlds(action.user, action.page)))
  22. }
  23. }
  24. override suspend fun emit(action: HelloWorldAction, state: HelloWorldState, data: Response<HelloWorld>?) =
  25. when(action) {
  26. is HelloWorldAction.Load -> {
  27. data?.let {
  28. val linkAttributes = it.headers().get(LinkKey)?.parseLinkNextAttributes()
  29. val nextPage = linkAttributes?.get("page")
  30. HelloWorldState.LoadSuccess(action.page, nextPage, it.body())
  31. } ?: HelloWorldState.LoadFailed(null)
  32. }
  33. }
  34. }
  35. init {
  36. HelloWorldDISingleton.getComponent(getApplication()).inject(this)
  37. }
  38. override fun bind(flow: Flow<HelloWorldState>): Flow<HelloWorldState> =
  39. flow
  40. .onStart { emit(HelloWorldState.Loading) }
  41. .catch { emit(HelloWorldState.LoadFailed(error = it.localizedMessage)) }
  42. }

And this is the corresponding Fragment:

  1. class HelloWorldFragment : Fragment(), OnMapReadyCallback {
  2. private lateinit var viewModel: HelloWorldViewModel
  3. private var map: GoogleMap? = null
  4. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  5. super.onViewCreated(view, savedInstanceState)
  6. viewModel = ViewModelProvider(this).get(HelloWorldViewModel::class.java)
  7. // `viewModel.state` is a [LiveData] object with the type constraint you configure in the view model.
  8. // In this case, the type constraint is [HelloWorldState] (a.k.a LiveData<HelloWorldState>).
  9. // Any emission from this [LiveData] object will call `render` with the emitted state.
  10. viewModel.state.observe(viewLifecycleOwner, ::render)
  11. // Call `dispatch` and pass in the action type you configured in the view model.
  12. // In this case, that's `HelloWorldAction`.
  13. viewModel.dispatch(HelloWorldAction.Load(user))
  14. //...
  15. }
  16. /**
  17. * Receives all state emissions produced from the view model that pass through the emitter.
  18. */
  19. private fun render(state: HelloWorldState) {
  20. when(state) {
  21. is HelloWorldState.Initial -> Unit
  22. is HelloWorldState.Loading -> renderLoadingUI()
  23. is HelloWorldState.LoadSuccess -> {
  24. updateUi(state)
  25. state.nextPage?.let {
  26. viewModel.dispatch(HelloWorldAction.Load(user, it))
  27. }
  28. }
  29. is HelloWorldState.LoadFailed -> renderLoadFailedUI(state.error)
  30. }
  31. }
  32. //...
  33. }