ABOUT ME

-

  • Android) BroadcastReciever와 LiveData로 실시간 네트워크 연결 감지하기
    Android 2022. 1. 17. 19:23


    NetworkInfo

    class NetworkManager(private val context : Context) {
        fun isOnline(): Boolean {
            val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
            return networkInfo?.isConnected == true
        }
    }
    • 기존에 네트워크 연결 상태를 감지하는 위와 같은 코드가 많이 사용됩니다.
    • activeNetworkInfo(getActiveNetworkInfo()) 메서드를 통해 처음 연결된 네트워크 인터페이스를 찾아서 이를 나타내는 NetworkInfo  인스턴스를 반환하거나 연결된 인터페이스가 없거나 사용 불가인 경우 null을 반환합니다.
    • 단점은 실시간으로 네트워크 연결 상태를 감지할 수 없습니다. 해당 코드가 호출되는 시점에서만 체크가 되어, 중간에 인터넷 연결이 끊어진다면 체크할 수 없습니다.
    • 또한, deprecated 되었습니다.

     

    BroadcastReceiver + LiveData

    • ViewModel에서는 Context 사용을 지양해야 합니다.
    • 그래서 LiveData로 네트워크 연결을 감지하여 그 값을 ViewModel에 전달하는 방법을 택했습니다.
    class ConnectivityWatcher(
        private val context: Context
    ) : LiveData<Boolean>() {
    
        private lateinit var networkCallback: ConnectivityManager.NetworkCallback
        private lateinit var broadcastReceiver: BroadcastReceiver
    
        override fun onActive() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
                networkCallback = createNetworkCallback()
                cm.registerDefaultNetworkCallback(networkCallback)
            } else {
                val intentFilter = IntentFilter(CONNECTIVITY_ACTION)
                broadcastReceiver = createBroadcastReceiver()
                context.registerReceiver(broadcastReceiver, intentFilter)
            }
        }
    
        override fun onInactive() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
                cm.unregisterNetworkCallback(networkCallback)
            } else {
                context.unregisterReceiver(broadcastReceiver)
            }
        }
    
        private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {
            @RequiresApi(Build.VERSION_CODES.M)
            override fun onCapabilitiesChanged(
                network: Network,
                networkCapabilities: NetworkCapabilities
            ) {
                val isInternet = networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)
                val isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)
                postValue(isInternet && isValidated)
            }
    
            override fun onLost(network: Network) {
                postValue(false)
            }
        }
    
        private fun createBroadcastReceiver() = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val isNoConnectivity = intent?.extras?.getBoolean(EXTRA_NO_CONNECTIVITY) ?: true
                postValue(!isNoConnectivity)
            }
        }
    }

     

    • BroadcastReceiver의 서브클래스인 NetworkReceiver로 네트워크의 연결이 변경되면 NetworkReceiver에서 CONNECTIVITY_ACTION을 활용해 현재 네트워크 상태를 가져와 그 값을 LiveData로 반환합니다.
    • LiveData의 onActive, onInActive 상태에 NetworkCallback의 등록 및 해제를 시켜줍니다.
    • NetworkCallback은 onAvailable, onLost, onLinkPropertiesChanged, onCapabilitiesChanged, onLosing 등의 메서드를 제공합니다.
    • 콜백을 등록 후 registerDefaultNetworkCallback() 또는 registerNetworkCallback()을 사용할 수 있습니다.​
    • 전자는 네트워크를 대상으로 하도록 미리 정의된 반면 후자는 추가 설정이 필요하지만 더 고급 기능을 제공합니다.

     

    ※ 하지만 이 방법은 애매한 연결을 감지하지 못합니다. 네트워크의 연결이 불안정하거나 연결이 되려다 말았다 하는 상황 등..

    Usage

    // ViewModel
    val isNetworkAvailable = MutableLiveData<Boolean>()
    
    // Activity
    ConnectivityWatcher(this).observe(this) { connection ->
        viewModel.isNetworkAvailable.value = connection
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    })

     

     

     

    반응형

    댓글

Designed by Me.