我很难理解如何迁移到谷歌认可的Android现代开发。在MVVM中,“业务逻辑”应该在ViewModel中,建议ViewModel的子类应该是Context不知道的,但大多数Android类需要Context才能提供任何功能。例如,如果没有Context(如WifiManager和ConnectivityManager),则无法获取对系统服务的引用。
举个例子,我试图为一个应用构建一个简单的架构,它只有一个按钮,可以根据ConnectivityManager文档中概述的活动默认网络链接来更改其图像。这个系统级操作应该在代码的“业务逻辑”区域处理,因为它与UI无关。它检测网络状态,改变了ViewModel中的一些状态数据,这应该是神奇的。由于一些Observable通知UI数据已更改并触发ComposeRecomposition更改按钮图像,因此更改按钮图像。我已经开始构建此应用程序,我将发布到目前为止我所拥有的内容。如果ViewModel是应用程序状态的仲裁者,那么创建一个没有上下文的ViewModel?这似乎是自相矛盾的。
ViewModel摘录:
enum class ConnectivityState {
WIFI,
CELLULAR,
DISCONNECTED
}
class MainActivityViewModel(context: Context) : ViewModel() {
private val logger: Logger = LoggerFactory.getLogger(this::class.java)
private val _uiState = MutableStateFlow(MainUIState())
val uiState: StateFlow<MainUIState> = _uiState.asStateFlow()
//TODO: how to perform system level business logic in ViewModel without Context reference?
//DESIRED: keep this 'business logic' away from UI. ViewModel should be appropriate but Context should not be used here?
private val wifiManager =
context.getSystemService(WIFI_SERVICE) as WifiManager
private val connectivityManager =
context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
/**
* a NetworkCallback which will be triggered when a change in network state is sent from the system
*/
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
_uiState.value.connectivityState = getConnectivityState()
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
_uiState.value.connectivityState = getConnectivityState()
}
override fun onLost(network: Network) {
super.onLost(network)
_uiState.value.connectivityState = getConnectivityState()
}
}
init {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
_uiState.value.connectivityState = getConnectivityState()
}
fun getConnectivityState(): ConnectivityState {
//these objects represent information about the currently active network
val currentNetwork = connectivityManager.getActiveNetwork()
val capabilities = connectivityManager.getNetworkCapabilities(currentNetwork)
val linkProperties = connectivityManager.getLinkProperties(currentNetwork)
if(currentNetwork == null) {
return ConnectivityState.DISCONNECTED
}
if(wifiManager.isWifiEnabled) {
//if wifi is enabled, it should be the current active network
val isWifi = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
if(isWifi != null) {
if(isWifi) {
//the current active link is a WiFi connection
logger.debug("Current active default network is WiFi.")
return ConnectivityState.WIFI
}
}
}
val isCellular = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
if(isCellular != null) {
if(isCellular) {
logger.info("Current active default network is cellular data.")
return ConnectivityState.CELLULAR
}
}
return ConnectivityState.DISCONNECTED
}
}
data class MainUIState(
var connectivityState: ConnectivityState = ConnectivityState.DISCONNECTED
)
字符串
活动摘录:
@Composable
fun wifiComposable() {
//WiFi button
Button(
onClick = {
MainActivity.openWifiSettings(LocalContext.current)
}
) {
//TODO: this image changes between DOWN, CELLULAR, WIFI images depending on connectivity state / source
Image(
//TODO: 'StateFlow.value should not be called within composition' why?
//DESIRED BEHAVIOR: when this state value changes, recomp and change image accordingly
painter = when(viewModel.uiState.value.connectivityState) {
ConnectivityState.WIFI -> painterResource(id = R.drawable.wifi_indicator)
ConnectivityState.CELLULAR -> painterResource(id = R.drawable.cellular_indicator)
ConnectivityState.DISCONNECTED -> painterResource(id = R.drawable.down_indicator)
},
modifier = Modifier.size(40.dp),
contentDescription = "WiFi Settings")
}
}
型
2条答案
按热度按时间41zrol4v1#
在Google推荐的架构方法中,推荐至少2层。(表示层-数据层)
表示- UI层
在屏幕上显示应用程序数据的UI层。
UI的作用是在屏幕上显示应用程序数据,同时也是用户交互的主要点。无论何时数据发生更改,无论是由于用户交互(如按下按钮)还是外部输入(如网络响应),UI都应更新以反映这些更改。实际上,UI是从数据层检索的应用程序状态的可视化表示。
ViewModel被视为状态保持器。
x1c 0d1x的数据
数据层
包含应用的业务逻辑并公开应用数据的数据层。
在你的情况下,将Network状态设置为集群。我建议使用Dagger Hilt注入Context。这毕竟是业务逻辑。对数据执行操作。作为Singleton访问它是一个很好的做法。
的
的
mepcadol2#
这就是关注点分离和理想的依赖注入发挥作用的地方。
你的视图模型应该关注为特定的视图提供正确的数据。这可以是一个小的组件,也可以是整个屏幕。
视图模型本身依赖于为它提供所需数据的其他类。这些类可以是进行Web API调用的服务,从DB加载数据或检查网络状态的系统服务。
服务应该像这样作为参数传递到视图模型中:
字符串
现在你的视图模型本身不需要上下文了,但是
NetworkService
仍然需要一个。型
此时,您可以将
NetworkService
设置为单例(在Kotlin中,您可以将class
交换为object
),并在Activity或Application类中对其进行初始化。然而,建议的方法是使用依赖注入(使用Hilt)来抽象所有这些。