英文:
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
变量存储了 true
或 false
(true
表示已连接到互联网,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 ->
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)
// Set current state
val currentState = getCurrentConnectivityState(connectivityManager)
trySend(currentState)
// Remove callback when not used
awaitClose {
// Remove listeners
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
// Creates a State<ConnectionState> 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("") }
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("")
}
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...
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 device how are able to connect with Ethernet
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ->
updateConnectionType("Conexão Ethernet")
// for check 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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论