如何将网络监听项目的Composable Activity转为普通Activity?collectAsState替代方案
问题描述
本项目用于监听网络连接,现需将基于Composable Activity的代码转换为普通Empty Activity,请问在普通Activity中可使用什么替代collectAsState的方案?
ConnectivityObserver 接口
package com.plcoding.observeconnectivity import kotlinx.coroutines.flow.Flow interface ConnectivityObserver { fun observe(): Flow<Status> enum class Status { Available, Unavailable, Losing, Lost } }
NetworkConnectivityObserver 实现类
package com.plcoding.observeconnectivity import android.content.Context import android.net.ConnectivityManager import android.net.Network import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch @OptIn(ExperimentalCoroutinesApi::class) class NetworkConnectivityObserver( private val context: Context ): ConnectivityObserver { private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager override fun observe(): Flow<ConnectivityObserver.Status> { return callbackFlow { val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { super.onAvailable(network) launch { send(ConnectivityObserver.Status.Available) } } override fun onLosing(network: Network, maxMsToLive: Int) { super.onLosing(network, maxMsToLive) launch { send(ConnectivityObserver.Status.Losing) } } override fun onLost(network: Network) { super.onLost(network) launch { send(ConnectivityObserver.Status.Lost) } } override fun onUnavailable() { super.onUnavailable() launch { send(ConnectivityObserver.Status.Unavailable) } } } connectivityManager.registerDefaultNetworkCallback(callback) awaitClose { connectivityManager.unregisterNetworkCallback(callback) } }.distinctUntilChanged() } }
原Composable类型的MainActivity代码
package com.plcoding.observeconnectivity import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.lifecycleScope import com.plcoding.observeconnectivity.ui.theme.ObserveConnectivityTheme import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach class MainActivity : ComponentActivity() { private lateinit var connectivityObserver: ConnectivityObserver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) connectivityObserver = NetworkConnectivityObserver(applicationContext) setContent { ObserveConnectivityTheme { val status by connectivityObserver.observe().collectAsState( initial = ConnectivityObserver.Status.Unavailable ) Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text(text = "Network status: $status") } } } } }
theme.kt代码
package com.plcoding.observeconnectivity.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.MaterialTheme import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.Composable private val DarkColorPalette = darkColors( primary = Purple200, primaryVariant = Purple700, secondary = Teal200 ) private val LightColorPalette = lightColors( primary = Purple500, primaryVariant = Purple700, secondary = Teal200 /* Other default colors to override background = Color.White, surface = Color.White, onPrimary = Color.White, onSecondary = Color.Black, onBackground = Color.Black, onSurface = Color.Black, */ ) @Composable fun ObserveConnectivityTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } MaterialTheme( colors = colors, typography = Typography, shapes = Shapes, content = content ) }
替代collectAsState的方案
在普通Activity中,有两种常用方案可以替代Compose的collectAsState来收集Flow并更新UI:
1. 直接用lifecycleScope收集Flow
利用Activity自带的lifecycleScope启动协程,结合onEach回调更新UI,无需手动管理协程生命周期:
class MainActivity : AppCompatActivity() { private lateinit var connectivityObserver: ConnectivityObserver private lateinit var statusTv: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 布局需包含id为status_tv的TextView statusTv = findViewById(R.id.status_tv) connectivityObserver = NetworkConnectivityObserver(applicationContext) // 收集Flow并更新UI connectivityObserver.observe() .onEach { status -> // 主线程更新UI statusTv.text = "Network status: $status" } .flowOn(Dispatchers.Main) // 确保回调在主线程执行 .launchIn(lifecycleScope) // 绑定Activity生命周期,自动取消 } }
2. 将Flow转换为LiveData观察
如果你更习惯使用LiveData,可以借助asLiveData扩展函数将Flow转为LiveData,再通过观察LiveData更新UI:
首先确保依赖中包含LiveData KTX:
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2"
然后修改Activity代码:
class MainActivity : AppCompatActivity() { private lateinit var connectivityObserver: ConnectivityObserver private lateinit var statusTv: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) statusTv = findViewById(R.id.status_tv) connectivityObserver = NetworkConnectivityObserver(applicationContext) // Flow转LiveData val networkStatusLiveData = connectivityObserver.observe() .asLiveData(Dispatchers.IO) // 在IO线程收集Flow // 观察LiveData更新UI(默认在主线程回调) networkStatusLiveData.observe(this) { status -> statusTv.text = "Network status: $status" } } }
关键注意点
- 两种方案都自动绑定Activity生命周期,无需手动取消收集,避免内存泄漏。
- 更新UI必须在主线程执行,第一种方案通过
flowOn(Dispatchers.Main)保证,第二种方案中LiveData的observe回调默认在主线程。
内容的提问来源于Stack Exchange,提问作者Ahmed Hossam
相关产品推荐
相关产品推荐

