인터넷 연결이 필요한 대부분의 앱이 실행 도중 네트워크 연결이 되지 않은 상태가 되거나 그 상태에서 API 요청을 하는 경우 네트워크가 연결을 확인해달라는 메시지가 앱에 나타난다.
카카오톡의 경우, 연결이 되지 않은 경우 상단에 연결이 될 때까지 네트워크 연결을 확인해달라는 메시지가 나타나고, 연결된 후에는 사라진다.
다른 앱에서는 재시도 버튼과 함께 메시지가 나타나거나 네트워크 연결이 되지 않은 상태에서 앱에 진입할 때만 메시지가 나타나고 API 요청 시에 다시 메시지 나타나는 등 네트워크 상태에 대한 대응 방식이 다양한 것 같다.
다음 4가지의 클래스를 사용하여 네트워크와 관련된 정보들을 얻을 수 있다.
ConnectivityManager
앱의 네트워크 상태를 알림
Network
기기와 연결된 네트워크 중 하나를 가리키는 클래스, ConnectivityMananger와 함께 사용하여 네트워크 정보를 수집하거나 소캣을 결합할 수 있음
LinkProperties
네트워크에 설치된 DNS 서버, 로컬 IP 주소, 네트워크 경로 목록 등 자세한 네트워크 연결 정보가 포함되어 있음
NetworkCapabilities
전송 및 네트워크에서 사용할 수 있는 기능과 같은 네트워크 속성 정보가 포함됨
나는 자세한 네트워크 정보가 아닌 네트워크 상태를 알아보는 것이 목적이기 때문에 LinkProperties, NetworkCapabilities는 사용하지 않을 것 같다.
일단 네트워크 상태에 대한 정보를 가져오기 위해서는 ConnectivityMananger 인스턴스를 생성해야 한다.
private val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager를 통해서 현재 연결된 네트워크 객체를 쉽게 가져올 수 있다.
val currentNetwork = connectivityManager.activeNetwork
현재 받아오는 activeNetwork가 null이라면 현재 연결된 네트워크가 없기 때문에 다음과 같은 메서드를 작성했다.
MainActivity 클래스에서 직접 만든 NetworkConnection 객체를 생성하고 getCurrentNetworkStatus 메서드를 호출하였다. Log 값을 통해서 결과를 확인해보았다.
첫 번째 결과는 와이파이, 데이터 모두 연결하지 않았을 때이다.
두 번째 결과는 와이파이를 다시 켜서 와이파이가 연결된 상태에서의 결과이다.
매번 함수를 호출하여서 순간적인 네트워크 상태를 확인하기에는 번거로울 수 있다. 그래서 NetworkManager에 NetworkCallback을 설정하여서 각 이벤트가 발생했을 때 알아서 동작을 처리할 수 있다.
다음과 같이 NetworkCallback 객체를 생성할 수 있다. 오버라이드 가능한 메서드들이 다양한데 onLost, onUnavailable, onAvailable 세 가지만 사용해보겠다.
private var networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onLost(network: Network) {
super.onLost(network)
}
override fun onUnavailable() {
super.onUnavailable()
}
override fun onAvailable(network: Network) {
super.onAvailable(network)
}
}
오버라이드한 메서드를 재정의한 NetworkCallback 객체를 만들었으면
connectivityManager.registerDefaultNetworkCallback(networkCallback)
connectivityManager에 NetworkCallback 객체를 등록하면 된다. 만약 더 이상 NetworkCallback을 사용하지 않는다면
connectivityManager.unregisterNetworkCallback(networkCallback)
connectivityManager 객체의 unregisterNetworkCallback 함수를 호출해서 등록된 콜백을 해제한다.
등록할 콜백 객체를 통해서 flow로 변환하면 flow 값에 따라서 UI를 생성하기 편하기 때문에 실시간 네트워크 상태에 대한 대응이 수월하다고 생각한다. 실제로 찾아봤을 때 많은 사람들이 networkCallback을 바탕으로 flow를 생성하는 것 같았다.
callback을 바탕으로 flow를 생성하는 flow builder인 callbackFlow가 존재한다. callbackFlow 빌더와 trySend, awaitClose를 통해서 다음과 같이 networkStatus라는 flow를 생성할 수 있다.
awaitClose는 flow가 cancel되거나 close되었을 때 호출된다. 그렇기 때문에 등록한 콜백을 해제하였다.
다음과 같이 networkConnection 객체의 networkStatus flow를 state로 변환하여 state 값에 따라서 네트워크 연결이 필요하다는 메시지의 다이얼로그를 표시할 수 있다.
이렇게 flow를 만든 후에 state로 변환하여 사용한다면 재시도 버튼이 필요없이 네트워크 상태에 따라서 다이얼로그가 나타나고 사라질 수 있다.
flow로 만들어서 현재 상태를 계속 관찰할 수 있으면 재시도 버튼이 필요없다고 느껴졌다. 그럼에도 불구하고 많은 앱에서 지속적으로 네트워크 상태를 관찰하지 않고 재시도 버튼을 통해서 네트워크 상태를 체크하는 것 같았다. 네트워크 상태를 계속 관찰하는 게 오버헤드가 크기 때문에 그렇게 한 것인지 궁금하다.