Android

Android) BroadcastReciever와 LiveData로 실시간 네트워크 연결 감지하기

가짜 개발자 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()
})

 

 

 

반응형