You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将网络监听项目的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:11:06