1、BaseActivity.kt
abstract class BaseActivity<VM : BaseViewModel<*>, VB : ViewBinding> : BaseViewModelActivity<VM, VB>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
observeViewState()
observeViewEvent()
}
protected abstract fun observeViewState()
private fun observeViewEvent() {
viewModel.collectEvent(this) {
collect {
when(it) {
is ViewEvent.ShowToast -> ToastUtil.showToast(this@BaseActivity, it.msg)
else -> onViewEvent(it)
}
}
}
}
protected open fun onViewEvent(event: ViewEvent) {
}
}
2、BaseViewModel.kt
open class BaseViewModel<VS: ViewState>(application: Application) : BaseViewModel(application) {
private val _viewState = MutableStateFlow(defaultState())
// val viewState = _viewState.asStateFlow()
private val _viewEvent = MutableSharedFlow<ViewEvent>()
// val viewEvent = _viewEvent.asSharedFlow()
private fun defaultState(): VS {
val parameterizedType = javaClass.genericSuperclass as ParameterizedType
val clazz = parameterizedType.actualTypeArguments[0] as Class<VS>
return clazz.newInstance()
}
protected fun setState(newState: VS) {
_viewState.update {
newState
}
}
protected fun setState(update: VS.() -> Unit) {
_viewState.update {
(it.clone() as VS).apply {
update()
}
}
}
protected fun sendEvent(event: ViewEvent) {
launch {
_viewEvent.emit(event)
}
}
fun collectState(lifecycleOwner: LifecycleOwner, action: StateCollector<VS>.() -> Unit) {
_viewState.collectState(lifecycleOwner) {
action()
}
}
fun collectEvent(lifecycleOwner: LifecycleOwner, action: EventCollector<ViewEvent>.() -> Unit) {
_viewEvent.collectEvent(lifecycleOwner) {
action()
}
}
/**
* 显示错误信息toast
*/
protected fun showToast(msg: String) {
sendEvent(ViewEvent.ShowToast(msg))
}
}
3、StateCollector.kt
fun <T : ViewState> Flow<T>.collectState(
lifecycleOwner: LifecycleOwner,
state: Lifecycle.State = Lifecycle.State.STARTED,
action: StateCollector<T>.() -> Unit
) {
StateCollector(this, lifecycleOwner, state).action()
}
class StateCollector<T : ViewState>(
private val flow: Flow<T>,
private val lifecycleOwner: LifecycleOwner,
private val state: Lifecycle.State,
) {
private val prop1Set = mutableSetOf<KProperty1<T, *>>()
private fun inProp1Set(p: KProperty1<T, *>):Boolean {
for (prop in prop1Set) {
if (prop.name == p.name) {
return true
}
}
return false
}
private fun areEquivalentExcludeSingleCollectProp(old: T, new: T): Boolean {
if (prop1Set.isNotEmpty()) {
val propList = old.javaClass.kotlin.members.filterIsInstance<KProperty1<T, *>>()
for (p in propList) {
if (inProp1Set(p)) continue
val oldValue = p.get(old)
val newValue = p.get(new)
if (oldValue != newValue) {
return false
}
}
return true
}
return old == new
}
fun collect(action: (T) -> Unit) {
//lifecycleOwner利用开启协程
lifecycleOwner.lifecycleScope.launch {
//控制订阅的生命周期
lifecycleOwner.repeatOnLifecycle(state) {
//如果单独收集的属性不参与比对,防止ui重复刷新
flow.distinctUntilChanged(::areEquivalentExcludeSingleCollectProp)
//收集元素
.collect { state ->
action(state)
}
}
}
}
fun <A> collect(
prop1: KProperty1<T, A>,
action: (A) -> Unit
) {
prop1Set.add(prop1)
//lifecycleOwner利用开启协程
lifecycleOwner.lifecycleScope.launch {
//控制订阅的生命周期
lifecycleOwner.repeatOnLifecycle(state) {
//只关注整个状态流中的某个元素(重点!利用反射机制找出需要的元素)
flow.map { prop1.get(it) }
//防抖,防止其他元素更新导致自身更新
.distinctUntilChanged()
//收集元素
.collect { partialState ->
action(partialState)
}
}
}
}
}
4、EventCollector.kt
fun <T : ViewEvent> Flow<T>.collectEvent(
lifecycleOwner: LifecycleOwner,
state: Lifecycle.State = Lifecycle.State.STARTED,
action: EventCollector<T>.() -> Unit
) {
EventCollector(this, lifecycleOwner, state).action()
}
class EventCollector<T : ViewEvent>(
private val flow: Flow<T>,
private val lifecycleOwner: LifecycleOwner,
private val state: Lifecycle.State,
) {
fun collect(action: (T) -> Unit) {
//lifecycleOwner利用开启协程
lifecycleOwner.lifecycleScope.launch {
//控制订阅的生命周期
lifecycleOwner.repeatOnLifecycle(state) {
//收集元素
flow.collect { event ->
action(event)
}
}
}
}
}
5、ViewState.kt
interface ViewState: Cloneable {
public override fun clone(): Any {
return super.clone()
}
}
6、ViewEvent.kt
interface ViewEvent {
class ShowToast(val msg: String): ViewEvent
}