특정 앱을 보면 연결이 끊기는 경우 팝업이 나오거나 연결이 안됐다는 화면이 나오는 것을 볼 수 있을 것이다.
이를 구현하기 위해서는 ConnectivityManager를 이용해 구현이 가능하다.
해당 구현 예제는 유튜브 강의를 통해 진행을 했다.
[versions]
lifecycleRuntimeKtx = "2.9.4"
[libraries]
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
Hilt나 koin같은 의존성 주입으로도 진행해도 되지만 간단하게 연결이 되는 여부만 판단하고 싶기 때문에 위 의존성을 추가
dependencies {
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
}
interface ConnectivityObserver {
val isConnected: Flow<Boolean>
}
class AndroidConnectivityObserver(
private val context: Context
) : ConnectivityObserver {
private val connectivityManager = context.getSystemService<ConnectivityManager>()
//isConnected는 연결 여부를 Flow형식으로 데이터형식이 변경될 때마다 trySend로 연결 여부를 보낸다.
//여기서 callbackFlow를 사용한 이유는 ConnectivityManager가 callback을 등록/해제해야 하고 callback에 따라 데이터를 보내야 하기 위해 사용
override val isConnected: Flow<Boolean>
get() = callbackFlow {
//ConnectivityManager.NetworkCallback은 안드로이드에서 네트워크 상태 변화를 감지할 때 사용됩니다.
val callback = object : ConnectivityManager.NetworkCallback() {
//네트워크의 세부 기능(capabilities)이 바뀌었을 때 호출됩니다.
//예를 들어 인터넷 연결이 안되다가 연결이 되는 상태로 바뀔 때
..
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, networkCapabilities)
//networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
//실제 인터넷 연결 여부 확인
val connected = networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_VALIDATED
)
trySend(connected)
}
//네트워크 요청을 했는데, 아예 사용 가능한 네트워크가 없을 때 호출됩니다.
override fun onUnavailable() {
super.onUnavailable()
trySend(false)
}
//이미 사용 중이던 네트워크 연결이 끊겼을 때 호출됩니다.
override fun onLost(network: Network) {
super.onLost(network)
trySend(false)
}
//네트워크가 사용 가능해졌을 때 호출됩니다.
//예: Wifi
override fun onAvailable(network: Network) {
super.onAvailable(network)
trySend(true)
}
}
connectivityManager?.registerDefaultNetworkCallback(callback)
awaitClose {
connectivityManager?.unregisterNetworkCallback(callback)
}
}
}
연결 상태 및 연결 측정 모니터링은 아래 공식 문서를 참조하면 좋을 것 같다.
https://developer.android.com/training/monitoring-device-state/connectivity-status-type?hl=ko
사실상 이 코드가 가장 중요한 코드다. 인터페이스는 아마 domain쪽에 들어갈 것 같고 AndroidConnectivityObserver는 data 모듈에서 applicationContext를 주입받아 사용하면 특정 화면에서 연결 여부를 확인하고 싶을 때 ViewModel에서 ConnectivityObserver를 주입받아서 사용하면 재사용성이 증가할 것이다.
class ConnectivityViewModel(
private val connectivityObserver: ConnectivityObserver
) : ViewModel() {
val isConnected = connectivityObserver.isConnected
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
false
)
}
hilt로 주입하거나, koin으로 주입해도 되지만 그것까지 사용할건 아니기 때문에 UI에서는 직접 factory에서 생성자 주입을 했다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
//직접 주입
val viewModel = viewModel<ConnectivityViewModel> {
ConnectivityViewModel(
connectivityObserver = AndroidConnectivityObserver(
//여기에는 applicationContext가 적합
context = applicationContext
)
)
}
ConnectManagerExTheme {
val isConnected by viewModel.isConnected.collectAsStateWithLifecycle()
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
contentAlignment = Alignment.Center
) {
Text(
text = "Connected? $isConnected"
)
}
}
}
}
}
}
실제로 동작하면 연결 상태를 끊으면 false, 다시 연결하면 true가 나오는 것을 볼 수 있다.
