
background
Cross page communication is a common scenario. We usually choose to use EventBus, but EventBus cannot perceive the life cycle and will call back when receiving messages. Therefore, with LiveData, LiveEventBus will be available soon. However, it also has disadvantages, such as the inability to switch receiving threads. Now SharedFlow is stable. Can we make a wave?
So there was FlowEventBus
Comparison of common message buses

Design conception
By learning how to migrate from LiveData to Kotlin data stream, we get the following ideas:
SharedFlow as event carrier:
advantage:
- Easily switch threads based on collaboration
- The sticky effect can be achieved through replay
- Can be subscribed by multiple observers
- Automatic clearing of events without observers does not create a backlog
Combined with Lifecycle awareness, the response time can be controlled. It can not only global events, but also single page communication without transmitting to other pages, such as internal communication of Activity and internal communication of Fragment.
Dependent library version
The key is that kotlinx coroutines > 1.4 X # and # lifecycle runtime KTX > 2.3 x
API
The events in the following examples are randomly defined classes, only the names defined to distinguish events during testing
Event sending
//Global scope postEvent(AppScopeEvent("form TestFragment")) //Fragment internal scope postEvent(fragment,FragmentEvent("form TestFragment")) //Activity # internal scope postEvent(requireActivity(),ActivityEvent("form TestFragment")) Copy code event listeners //Receive Activity Scope events observeEvent<ActivityEvent>(scope = requireActivity()) { ... } //Receive Fragment Scope event observeEvent<FragmentEvent>(scope = fragment) { ... } //Receive App Scope events observeEvent<AppScopeEvent> { ... }
Like ObserveForever: //In this case, you need to specify the coordination range observeEvent<GlobalEvent>(scope = coroutineScope) { ... }
Delayed transmission
postEvent(CustomEvent(value = "Hello Word"),1000) Copy code Thread switching observeEvent<ActivityEvent>(Dispatchers.IO) { ... }
Specifies the minimum perceptible state of life
observeEvent<ActivityEvent>(minActiveState = Lifecycle.State.DESTROYED) { ... }
Sticky listening
observeEvent<GlobalEvent>(isSticky = true) { ... }
Remove sticky events
removeStickyEvent(StickyEvent::class.java) removeStickyEvent(fragment,StickyEvent::class.java) removeStickyEvent(activity,StickyEvent::class.java)
principle
The above functions rely on the SharedFlow and Lifecycle of Kotlin collaboration, so it is very simple to implement.
Viscous event
MutableSharedFlow<Any>( replay = if (isSticky) 1 else 0, extraBufferCapacity = Int.MAX_VALUE //Avoid data sending failure due to suspension )
Life cycle perception
fun <T> LifecycleOwner.launchWhenStateAtLeast( minState: Lifecycle.State, block: suspend CoroutineScope.() -> T ) { lifecycleScope.launch { lifecycle.whenStateAtLeast(minState, block) } }
Switch thread
whenStateAtLeast: since the block executed is on the main thread by default, you need to manually switch threads:
lifecycleOwner.launchWhenStateAtLeast(minState) { flow.collect { value -> lifecycleOwner.lifecycleScope.launch(dispatcher) { onReceived.invoke(value as T) } } }
Delay event
viewModelScope.launch { delay(time) flow.emit(value) }
Orderly distribution
Flow itself is orderly
Global singleton
The use of global ViewModel is mainly due to the ViewModelScope, which can avoid the use of GlobalScope. If you want to communicate with internal components on a single page, you can use the ViewModel of ActivityScope:
object ApplicationScopeViewModelProvider : ViewModelStoreOwner { private val eventViewModelStore: ViewModelStore = ViewModelStore() override fun getViewModelStore(): ViewModelStore { return eventViewModelStore } private val mApplicationProvider: ViewModelProvider by lazy { ViewModelProvider( ApplicationScopeViewModelProvider, ViewModelProvider.AndroidViewModelFactory.getInstance(EventBusInitializer.application) ) } fun <T : ViewModel> getApplicationScopeViewModel(modelClass: Class<T>): T { return mApplicationProvider[modelClass] } }
There are two map s inside the ViewModel, viscous and non viscous:
internal class EventBusViewModel : ViewModel() { private val eventFlows: HashMap<String, MutableSharedFlow<Any>> = HashMap() private val stickyEventFlows: HashMap<String, MutableSharedFlow<Any>> = HashMap() ... }
Advanced notes of Android advanced development system, latest interview review notes PDF, My GitHub
end of document
If you have any opinions on the article or any technical problems, please leave a message in the comment area for discussion!