如何在Jetpack Compose中创建后台进程?

huangapple go评论61阅读模式
英文:

how can i make a background process in jetpack compose?

问题

我正在创建一个实时的互联网连接检查。我怎样可以在Jetpack Compose中创建一个后台进程?最好的方法是什么?我正在使用以下代码:

val connectionType = mutableStateOf("")

suspend fun run(context: Context) = withContext(Dispatchers.IO) {
    // 调用连接管理器
    val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    while (true) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val nw = connectivityManager.activeNetwork
            val actNw = connectivityManager.getNetworkCapabilities(nw)
            if (actNw != null) {
                when {
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> connectionType.value =
                        "连接 WIFI"
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> connectionType.value =
                        "连接数据蜂窝"
                    //对于其他设备,如能够通过以太网连接的设备
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> connectionType.value =
                        "连接以太网"
                    //检查蓝牙上的互联网
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> connectionType.value =
                        "连接蓝牙"
                    else -> connectionType.value = "没有连接"

                }
            } else {
                connectionType.value = "没有连接"
            }
        } else {
            val netInfo = connectivityManager.allNetworkInfo

            for (ni in netInfo) {
                if (ni.typeName.equals("WIFI", ignoreCase = true))
                    if (ni.isConnected) connectionType.value = "连接 WIFI"
                if (ni.typeName.equals("MOBILE", ignoreCase = true))
                    if (ni.isConnected) connectionType.value =
                        "连接数据蜂窝"
                    else {
                        connectionType.value = "没有连接"
                    }
            }
        }
        delay(1000)
    }
}

@Composable
fun ConnectionChecker() {
    val context = LocalContext.current

    connectionType.value = remember {
        connectionType.value
    }

    LaunchedEffect(Unit) {
        run(context)
    }
}

它的功能完美,但从性能的角度来看,是否有更好的方法?

英文:

I'm creating a real-time internet connection check. how can i make a background process in jetpack compose? What would be the best way to do this? I am using the following code:

   val connectionType = mutableStateOf("")

    suspend fun run(context:Context) = withContext(Dispatchers.IO) {
        // Invoking the Connectivity Manager
        val connectivityManager =
            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        while (true) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                val nw = connectivityManager.activeNetwork
                val actNw = connectivityManager.getNetworkCapabilities(nw)
                if (actNw != null) {
                    when {
                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> connectionType.value =
                            "Conexão WIFI"
                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> connectionType.value =
                            "Conexão Dados Celular"
                        //for other device how are able to connect with Ethernet
                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> connectionType.value =
                            "Conexão Ethernet"
                        //for check internet over Bluetooth
                        actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> connectionType.value =
                            "Conexão Bluetooth"
                        else -> connectionType.value = "Sem conexão"

                    }
                } else {
                    connectionType.value = "Sem conexão"
                }
            } else {
                val netInfo = connectivityManager.allNetworkInfo

                for (ni in netInfo) {
                    if (ni.typeName.equals("WIFI", ignoreCase = true))
                        if (ni.isConnected) connectionType.value = "Conexão WIFI"
                    if (ni.typeName.equals("MOBILE", ignoreCase = true))
                        if (ni.isConnected) connectionType.value =
                            "Conexão de Dados Celular"
                        else {
                            connectionType.value = "Sem conexão"
                        }
                }
            }
            delay(1000)
        }
    }

    @Composable
    fun ConnectionChecker() {
        val context = LocalContext.current

        connectionType.value = remember {
            connectionType.value
        }

        LaunchedEffect(Unit) {
            run(context)
        }

    }

It works perfectly but in terms of performance is there a better way?

答案1

得分: 3

让我告诉你一种更好的在Jetpack Compose中检查互联网连接的方法:

首先创建一个名为 NetworkUtility.kt 的文件,将下面的代码粘贴到该文件中。这将返回连接状态(可用、不可用)。

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow

val Context.currentConnectivityState: ConnectionState
    get() {
        val connectivityManager =
            getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        return getCurrentConnectivityState(connectivityManager)
    }

private fun getCurrentConnectivityState(
    connectivityManager: ConnectivityManager
): ConnectionState {
    val connected = connectivityManager.allNetworks.any { network ->
        connectivityManager.getNetworkCapabilities(network)
            ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            ?: false
    }

    return if (connected) ConnectionState.Available else ConnectionState.Unavailable
}

@ExperimentalCoroutinesApi
fun Context.observeConnectivityAsFlow() = callbackFlow {
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    val callback = NetworkCallback { connectionState -> trySend(connectionState) }

    val networkRequest = NetworkRequest.Builder()
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .build()

    connectivityManager.registerNetworkCallback(networkRequest, callback)

    // 设置当前状态
    val currentState = getCurrentConnectivityState(connectivityManager)
    trySend(currentState)

    // 当不再使用时移除回调
    awaitClose {
        // 移除监听器
        connectivityManager.unregisterNetworkCallback(callback)
    }
}

fun NetworkCallback(callback: (ConnectionState) -> Unit): ConnectivityManager.NetworkCallback {
    return object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            callback(ConnectionState.Available)
        }

        override fun onLost(network: Network) {
            callback(ConnectionState.Unavailable)
        }
    }
}

@ExperimentalCoroutinesApi
@Composable
fun connectivityState(): State<ConnectionState> {
    val context = LocalContext.current

    // 使用当前连接状态创建一个 State<ConnectionState> 作为初始值
    return produceState(initialValue = context.currentConnectivityState) {
        // 在协程中,可以进行挂起调用
        context.observeConnectivityAsFlow().collect { value = it }
    }
}

sealed class ConnectionState {
    object Available : ConnectionState()
    object Unavailable : ConnectionState()
}

现在,你需要在你的可组合函数中处理上述状态。前往你的可组合函数,并将以下代码写入其中。

val connection by connectivityState()
val isConnected = connection == ConnectionState.Available

isConnected 变量存储了 truefalsetrue 表示已连接到互联网,false 表示未连接)。

如果未连接到互联网,则显示以下消息。

@Composable
fun InternetConnection(modifier: Modifier = Modifier) {
    Box(
        modifier = modifier
            .fillMaxWidth()
            .background(redColor), contentAlignment = Center
    ) {
        Text15_500(
            text = stringResource(R.string.internet_connection),
            modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, start = 10.dp)
        )
    }
}

希望对你有所帮助!👍🏻🌟

英文:

Let me tell you a better way to check internet connection in jetpack compose

First make NetworkUtility.kt file and paste the below code there. This will return the connection state (Available , UnAvailable).

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
val Context.currentConnectivityState: ConnectionState
get() {
val connectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return getCurrentConnectivityState(connectivityManager)
}
private fun getCurrentConnectivityState(
connectivityManager: ConnectivityManager
): ConnectionState {
val connected = connectivityManager.allNetworks.any { network -&gt;
connectivityManager.getNetworkCapabilities(network)
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
?: false
}
return if (connected) ConnectionState.Available else ConnectionState.Unavailable
}
@ExperimentalCoroutinesApi
fun Context.observeConnectivityAsFlow() = callbackFlow {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val callback = NetworkCallback { connectionState -&gt; trySend(connectionState) }
val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(networkRequest, callback)
// Set current state
val currentState = getCurrentConnectivityState(connectivityManager)
trySend(currentState)
// Remove callback when not used
awaitClose {
// Remove listeners
connectivityManager.unregisterNetworkCallback(callback)
}
}
fun NetworkCallback(callback: (ConnectionState) -&gt; Unit): ConnectivityManager.NetworkCallback {
return object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
callback(ConnectionState.Available)
}
override fun onLost(network: Network) {
callback(ConnectionState.Unavailable)
}
}
}
@ExperimentalCoroutinesApi
@Composable
fun connectivityState(): State&lt;ConnectionState&gt; {
val context = LocalContext.current
// Creates a State&lt;ConnectionState&gt; with current connectivity state as initial value
return produceState(initialValue = context.currentConnectivityState) {
// In a coroutine, can make suspend calls
context.observeConnectivityAsFlow().collect { value = it }
}
}
sealed class ConnectionState {
object Available : ConnectionState()
object Unavailable : ConnectionState()
}

Now you have to handle above state in your composable function . Go to your composable function and write the below code there .

 val connection by connectivityState()
val isConnected = connection == ConnectionState.Available

The isConnected variable stores true or false (true means connected with internet and false means not connected)

if (!isConnected) InternetConnection()

if it's not connected with internet just show below msg.

@Composable
fun InternetConnection(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.background(redColor), contentAlignment = Center
) {
Text15_500(
text = stringResource(R.string.internet_connection),
modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, start = 10.dp)
)
}
}

Hope it's helpful 👍🏻

答案2

得分: 1

This code looks fine, but there are some refactors I can suggest.

connectionType can be declared like this:

var connectionType by remember { mutableStateOf("") }

And you can rename your run method to checkInternetConnection and place it outside of this Activity or Fragment. You can keep it inside some utility for more reusability. Please don't pass the context to it. Instead, you can create the connectivityManager inside the LaunchedEffect and pass it as a function, getting the result as a lambda to make it more readable.

After the refactor, your ConnectionChecker will look like this:

@Composable
fun ConnectionChecker() {
    var connectionType by remember { mutableStateOf("") }
    val context = LocalContext.current
    LaunchedEffect(Unit) {
        val connectivityManager =
            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        checkInternetConnection(connectivityManager) {
            connectionType = it
        }
    }
    Text(text = "Hello $connectionType!")
}

And your checkInternetConnection will look like this:

suspend fun checkInternetConnection(
    connectivityManager: ConnectivityManager,
    updateConnectionType: (String) -> Unit
) = withContext(Dispatchers.IO) {
    // Invoking the Connectivity Manager
    while (true) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val nw = connectivityManager.activeNetwork
            val actNw = connectivityManager.getNetworkCapabilities(nw)
            if (actNw != null) {
                when {
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ->
                        updateConnectionType("Conexão WIFI")

                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ->
                        updateConnectionType("Conexão Dados Celular")

                    // for other devices that are able to connect with Ethernet
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ->
                        updateConnectionType("Conexão Ethernet")

                    // for checking internet over Bluetooth
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) ->
                        updateConnectionType("Conexão Bluetooth")

                    else -> updateConnectionType("Sem conexão")
                }
            } else {
                updateConnectionType("Sem conexão")
            }
        } else {
            val netInfo = connectivityManager.allNetworkInfo

            for (ni in netInfo) {
                if (ni.typeName.equals("WIFI", ignoreCase = true)) {
                    if (ni.isConnected) updateConnectionType("Conexão WIFI")
                }
                if (ni.typeName.equals("MOBILE", ignoreCase = true)) {
                    if (ni.isConnected) updateConnectionType("Conexão de Dados Celular")
                    else {
                        updateConnectionType("Sem conexão")
                    }
                }
            }
            delay(1000)
        }
    }
}
英文:

This code looks fine only some refactor I can suggest.
connectionType can be declared as this
var connectionType by remember { mutableStateOf(&quot;&quot;) }

and your run method can be renamed as checkInternetConnection method and place it out of this Activity or Fragment. You can keep it inside some utility, so it provide more re-usability. Please dont pass the context to that, you can create the connectivityManager inside the LaunchedEffect and pass it that function, and get the result as lambda so it looks more readable too.

After refactor your ConnectionChecker will look like this.

@Composable
fun ConnectionChecker() {
var connectionType by remember {
mutableStateOf(&quot;&quot;)
}
val context = LocalContext.current
LaunchedEffect(Unit) {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
checkInternetConnection(connectivityManager) {
connectionType = it
}
}
Text(text = &quot;Hello $connectionType!&quot;)
}

and your checkInternetConnection will look like...

suspend fun checkInternetConnection(connectivityManager: ConnectivityManager, updateConnectionType: (String) -&gt; Unit) =
withContext(Dispatchers.IO) {
// Invoking the Connectivity Manager
while (true) {
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.M) {
val nw = connectivityManager.activeNetwork
val actNw = connectivityManager.getNetworkCapabilities(nw)
if (actNw != null) {
when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -&gt;
updateConnectionType(&quot;Conex&#227;o WIFI&quot;)
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -&gt;
updateConnectionType(&quot;Conex&#227;o Dados Celular&quot;)
// for other device how are able to connect with Ethernet
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -&gt;
updateConnectionType(&quot;Conex&#227;o Ethernet&quot;)
// for check internet over Bluetooth
actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -&gt;
updateConnectionType(&quot;Conex&#227;o Bluetooth&quot;)
else -&gt; updateConnectionType(&quot;Sem conex&#227;o&quot;)
}
} else {
updateConnectionType(&quot;Sem conex&#227;o&quot;)
}
} else {
val netInfo = connectivityManager.allNetworkInfo
for (ni in netInfo) {
if (ni.typeName.equals(&quot;WIFI&quot;, ignoreCase = true)) {
if (ni.isConnected) updateConnectionType(&quot;Conex&#227;o WIFI&quot;)
}
if (ni.typeName.equals(&quot;MOBILE&quot;, ignoreCase = true)) {
if (ni.isConnected) updateConnectionType(&quot;Conex&#227;o de Dados Celular&quot;)
else {
updateConnectionType(&quot;Sem conex&#227;o&quot;)
}
}
}
}
delay(1000)
}
}

huangapple
  • 本文由 发表于 2023年2月24日 06:03:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75550781.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定