[Android] ConnectivityManager를 활용한 네트워크 상태 모니터링 기능 구현하기
구현 영상
다음과 같이 앱 실행 중 네트워크 연결 상태를 실시간으로 감지하고, 이를 StateFlow로 외부에 제공하는 NetworkMonitorManager를 구현했습니다.
영상의 pull-to-refresh 기능의 경우 다음의 링크를 참고해주세요.
[Android] PullToRefreshBox를 활용한 리스트 pull-to-refresh 기능 구현하기
구현 영상다음과 같이 리스트에 pull-to-refresh 기능을 제공하여, 사용자가 리스트를 새로고침할 수 있도록 구현했습니다. 1. PullToRefreshBox란?PullToRefreshBox는 Material3의 실험적 API로, Compose에서 스와이
marchbreeze.tistory.com
1. ConnectivityManager란?
안드로이드 시스템의 핵심 서비스로, 네트워크 상태 조회, 네트워크 변경 알림, 특정 네트워크 요청 등을 처리합니다.
사용을 위해서는 네트워크 접근 권한이 필요합니다.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
시스템 서비스로부터 ConnectivityManager 인스턴스 획득하여 사용할 수 있습니다.
val connectivityManager = context
.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
(1) 네트워크 상태 조회
// 2) 기본 네트워크 객체
val network: Network? = connectivityManager.activeNetwork
// 3) 네트워크 세부 능력 조회
val caps: NetworkCapabilities? = network
?.let(connectivityManager::getNetworkCapabilities)
// 4) 주요 Capability 및 Transport 확인
val isValidated = caps?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
val isMetered = caps?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) == false
val isWifi = caps?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
val isCellular = caps?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
- 연결이 되어있으면 객체에 Network 정보가 있고, 연결이 안 되어 있다면 null이 반환됩니다.
- hasCapability : 인터넷 사용 가능 여부, 실제 인터넷 연결 여부, 비과금(ex. 와이파이) 연결 여부 등을 확인할 수 있습니다.
- hasTransport : WIFI, CELLULAR, VPN, BLUETOOTH 등 물리적/가상 전송 수단이 구분되어 있습니다.
(2) 네트워크 변경 감지
기존에는 BroadcastReceiver의 CONNECTIVITY_ACTION을 사용할 수 있었지만, Android 26 이후 백그라운드 제약이 생겨 동작이 보장되지 않아, 사용이 지양됩니다.
대신, 앱이 실행되는 동안 연결 상태가 바뀔 때 즉시 대응할 수 있도록, NetworkCallback API를 권장합니다.
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
// 네트워크 연결 가능
}
override fun onLost(network: Network) {
// 네트워크 연결 끊김
}
override fun onCapabilitiesChanged(
network: Network, caps: NetworkCapabilities
) {
// 예: metered 여부, 속도 변경
}
override fun onLinkPropertiesChanged(
network: Network, props: LinkProperties
) {
// 예: IP, DNS, MTU 변경
}
}
// 기본 요청: 인터넷 가능 네트워크 모니터링
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
// 등록 및 해제
connectivityManager.registerNetworkCallback(request, callback)
connectivityManager.unregisterNetworkCallback(callback)
2. 싱글톤 형식의 NetworkMonitorManager 구현
앱 전체에서 싱글톤으로 관리하며, StateFlow으로 연결 여부를 외부에 제공하는 유틸리티 클래스를 제작했습니다.
(1) NetworkMonitorManager 설정
object NetworkMonitorManager {
private lateinit var cm: ConnectivityManager
private val _isConnected = MutableStateFlow(true)
val isConnected: StateFlow<Boolean> = _isConnected
// 인터넷 가능 네트워크만 필터
private val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
private val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
_isConnected.value = true
}
override fun onLost(network: Network) {
_isConnected.value = false
}
}
fun registerNetworkCallback(context: Context) {
cm = context.getSystemService(
Context.CONNECTIVITY_SERVICE
) as ConnectivityManager
// 콜백 등록
cm.registerNetworkCallback(request, callback)
// 초기 상태 설정
val caps = cm.activeNetwork
?.let(cm::getNetworkCapabilities)
_isConnected.value = caps
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}
fun unRegisterNetworkCallback() {
cm.unregisterNetworkCallback(callback)
}
}
- StateFlow로 isConnected 상태를 제공해 반응형 UI 구성이 가능합니다.
- 등록 시 바로 초기값을 읽어와 앱 실행 직후 올바른 상태를 알 수 있습니다.
- 인터넷 필터링만 설정했으므로, 와이파이·셀룰러·VPN 등 모든 인터넷 가능 전송 수단을 포괄합니다.
(2) Compose 화면 연동
DisposableEffect와 collectAsStateWithLifecycle을 활용해, 생명주기에 맞게 안전하게 등록·해제하고, 상태 변화를 구독합니다.
@Composable
fun MainContent(viewModel: MainViewModel) {
val context = LocalContext.current
// 1) 네트워크 콜백 등록/해제
DisposableEffect(Unit) {
NetworkMonitorManager.registerNetworkCallback(context)
onDispose { NetworkMonitorManager.unRegisterNetworkCallback() }
}
// 2) StateFlow 구독
val isConnected by NetworkMonitorManager.isConnected.collectAsStateWithLifecycle()
// 3) ViewModel에 전달
LaunchedEffect(isConnected) {
iewModel.onIntent(MainIntent.NetworkChangeMonitored(isConnected))
}
// 4) UI 렌더링
if (!isConnected) {
// 네트워크 오류 메시지 또는 채팅 불가 화면
}
// ... 나머지 UI
}
- DisposableEffect: Compose에서 Side Effect를 라이프사이클에 맞춰 등록‧해제합니다.
- collectAsStateWithLifecycle: Lifecycle‑aware하게 StateFlow를 State로 구독합니다.
- LaunchedEffect: isConnected 값이 변경될 때마다 ViewModel로 Intent를 보냅니다.
다음과 같이 ConnectivityManager와 NetworkCallback API를 활용해서, 네트워크 변화에 민감한 앱에서 실시간 모니터링과 회복 로직을 쉽게 구현할 수 있습니다.
참고 자료 :
네트워크 상태 읽기 | Connectivity | Android Developers