graph LR
subgraph 抽象客观事物
页面开发任务-->|抽象出模型|数据处理并展示
end
subgraph 发现客观问题
数据处理并展示-->|发现问题|splitArch(需要分离界面和数据,分层架构)
数据处理并展示-->|发现问题|数据处理需要考虑界面生命周期
数据处理并展示-->|发现问题|ViewController需要声明式更新
数据处理并展示-->|发现问题|数据需要保留
数据处理并展示-->|发现问题|需要处理异步任务("需要处理异步任务,结构化并发")
数据处理并展示-->|发现问题|需要数据源emit多数据项
数据处理并展示-->|发现问题|需要抽象数据策略
数据处理并展示-->|发现问题|callback影响可读性
数据处理并展示-->|发现问题|层间依赖内部引用不灵活
数据处理并展示-->|发现问题|IOC时模板代码且无scope
end
subgraph 解决问题
splitArch-->|解决方案|MVC,MVP,MVVM("MVC,MVP,MVVM(数据驱动)")
数据处理需要考虑界面生命周期-->|解决方案|LifecycleAware组件
ViewController需要声明式更新-->|解决方案|DataBinding
数据需要保留-->|解决方案|ViewModel等三段式保留方案
需要处理异步任务-->|解决方案|Coroutine1(Coroutine)
需要数据源emit多数据项-->|解决方案|Flow
需要抽象数据策略-->|解决方案|NetworkBoundResource类族
callback影响可读性-->|解决方案|Coroutine2(Coroutine,Flow)
层间依赖内部引用不灵活-->|解决方案|IOC传入依赖
IOC时模板代码且无scope-->|解决方案|DI框架,如Hilt
end
Jetpack架构组件的通用落地设计如下
Activity和Fragment是同样的继承结构设计,以Activity为例:
classDiagram
class CommonActivity {
+inflateContent(savedInstanceState: Bundle?)*
+init(savedInstanceState: Bundle?)*
}
class CommonInflateActivity {
+inflateContent(savedInstanceState: Bundle?)
+getLayoutId()*Int
}
class DataBindingActivity~VDB : ViewDataBinding~{
+inflateContent(savedInstanceState: Bundle?)
+init(savedInstanceState: Bundle?)
-bindVariable(activityDataBinding: VDB)
+getLayoutId()* Int
+getVariableMap()* Map
}
class ViewModelCommonActivity~VM : BaseViewModel~ {
~VM viewModel
}
class ViewModelBindingActivity~VDB : ViewDataBinding, VM : BaseViewModel~ {
~VM viewModel
+getVariableMap()Map
+getViewModelVariableId()*Int
}
BaseActivity<|--CommonActivity
CommonActivity<|--CommonInflateActivity
CommonActivity<|--DataBindingActivity
CommonInflateActivity<|--ViewModelCommonActivity
DataBindingActivity<|--ViewModelBindingActivity
val userNameLiveData = liveData {
emit(userRepository.getUserNameFromCache())
} as MutableLiveData //two way data binding
solution1: use liveData coroutine and reObserve only when user event happens, must remove previous observer
solution2: use _ private mutableLiveData and public liveData, use fun updateXxx() = viewModelScope.launch(Dispatchers) { update private mutableLiveData}
//expose immutable LiveData to ViewController when one way data binding
private val _uidLiveData = MutableLiveData<String>()
val uidLiveData: LiveData<String>
get() = _uidLiveData
fun updateLoginData() {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
userRepository.login(userNameLiveData.value, passwordLiveData.value)
.collect {
emit(it, _uidLiveData)//use kotlin extension
}
}
}
graph LR
subgraph Encapsulation purpose
Target("generic parse Resource<> data struct and emit")
end
subgraph Encapsulation way
Target-->Extend("1. Extend: Implement LiveDataScope logic alone, because CoroutineLiveData is internel")
Target-->Combine/Delegate("2. Combine/Delegate: use Transformations/MediatorLiveData, custom .map and Observer")
Target-->FunctionExtension("3. FunctionExtension")
end
@Singleton
class UserRepository @Inject constructor(
private val userInMemoryDataSource: UserInMemoryDataSource,
private val userLocalDataSource: UserLocalDataSource,
private val userRemoteDataSource: UserRemoteDataSource
) {
classDiagram
class IFlowDataStrategy~ResultType~ {
+loadAsFlow: Flow~Resource~ResultType~~
}
class BaseFlowDataStrategy~ResultType~ {
-resultSource: Resource<ResultType>? = null
+FlowCollector<Resource<ResultType>>.emitIfDifferent(newValue: Resource<ResultType>)
}
class NetworkBoundResource~ResultType, RequestType~ {
+loadAsFlow:Flow<Resource<ResultType>>
}
class SimpleResource~ResultType~ {
+loadAsFlow:Flow<Resource<ResultType>>
}
class SimpleResourceFlow~ResultType~ {
+loadAsFlow:Flow<Resource<ResultType>>
}
class IFlowDataLoader~T~ {
+load:Flow<Resource<T>>
}
class IDataLoader~T~ {
+load:Resource<T>
}
IFlowDataStrategy<|--BaseFlowDataStrategy
BaseFlowDataStrategy<|--NetworkBoundResource
BaseFlowDataStrategy<|--SimpleResource
BaseFlowDataStrategy<|--SimpleResourceFlow
IFlowDataLoader<|--SimpleResourceFlow
IDataLoader<|--SimpleResource
optimizeofnetworkboundresource5.6.
graph TB
View("ViewController")-->|reference|ViewModel-->|reference|Repository-->|reference|LocalDataSource
Repository("DomainRepository")-->|reference|RemoteDataSource
ViewModel("PageViewModel")-->|LiveData,eq: Callback|View
Repository-->|Flow,eq: Stream|ViewModel