Al crear una aplicación que requiere de internet, el alguna parte del código se requiere detectar si el dispositivo tiene internet disponible o bien si la app no funciona sin conexión a internet lo mejor es supervisar en tiempo real el estado de la conexión.
Permisos necesarios
Se debe añadir los siguientes permisos para detectar la conexión a internet
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
NetworkUtils
Métodos de ayuda para la deteción de conectividad de una red y verificar si hay internet disponible.
- isNetworkAvailable: para obtener si el dispositivo está conectado a una red
- isInternetRecheable: para verificar la conexión a internet
- openInternetConnectivity: para abrir el panel de conexión a una red del sistema android
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.provider.Settings
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat.startActivity
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
object NetworkUtils {
@RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val activeNetwork = connectivityManager.activeNetwork ?: return false
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
return when {
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
} else @Suppress("deprecation") {
val activeNetwork = connectivityManager.activeNetworkInfo ?: return false
return activeNetwork.isConnectedOrConnecting
}
}
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET])
fun isInternetReachable(context: Context): Boolean {
if (isNetworkAvailable(context)) {
//Network is available, check if internet is reachable
try {
val httpConnection: HttpURLConnection = URL("https://clients3.google.com/generate_204")
.openConnection() as HttpURLConnection
httpConnection.setRequestProperty("User-Agent", "Android")
httpConnection.setRequestProperty("Connection", "close")
httpConnection.connectTimeout = 1500
httpConnection.connect()
return httpConnection.responseCode == 204
} catch (e: IOException) {
e.printStackTrace()
}
}
return false
}
fun openInternetConnectivityPanel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
startActivity(context, panelIntent, null)
}
}
}
Verificar si hay conectividad
Caso de uso para verificar si hay salida a internet, usando el método isInternetRecheable
GlobalScope.async {
if (NetworkUtils.isInternetReachable(requireContext())) {
//Has Internet
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
} else {
//No Internet found
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.error_internet_connection), Toast.LENGTH_SHORT).show()
}
}
}
Verificar si hay red disponible
Caso de uso para verificar si hay conexión a una red y luego testear si tiene capacidad de Internet, primero se usará el método isNetworkAvailable, si hay una red establecida se llamará isInternetRecheable, si no está disponible una red, mostrará una snackbar con la opción de abrir el panel de conectividad del sistema android con el método openInternetConnectivity
GlobalScope.async {
if (NetworkUtils.isInternetReachable(this@MainActivity)) {
withContext(Dispatchers.Main) {
Toast.makeText(this@MainActivity, "eurekaaaaaa!", Toast.LENGTH_SHORT).show()
}
} else {
if (!NetworkUtils.isNetworkAvailable(this@MainActivity)) {
Snackbar.make(view, getString(R.string.error_network_not_available), Snackbar.LENGTH_LONG)
.setAnchorView(R.id.fab)
.setAction(getString(R.string.error_network_not_available_action)) {
NetworkUtils.openInternetConnectivityPanel(this@MainActivity)
}.show()
} else {
withContext(Dispatchers.Main) {
Toast.makeText(this@MainActivity, getString(R.string.error_internet_connection), Toast.LENGTH_SHORT).show()
}
}
}
}
Supervisar la conexión a internet
En caso de querer supervisar en tiempo real el estado a internet, si el usuario cambia de red sin capacidad de Internet etc…, para ello se usa un livedata ConnectionLiveData que monitorice el estado
import android.content.Context
import android.content.Context.CONNECTIVITY_SERVICE
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
import android.net.NetworkRequest
import androidx.lifecycle.LiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
import java.net.InetSocketAddress
import javax.net.SocketFactory
class ConnectionLiveData(val context: Context) : LiveData<Boolean>() {
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
private val connectivityManager =
context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
private val validNetworks: MutableSet<Network> = HashSet()
private fun checkValidNetworks() {
postValue(validNetworks.size > 0)
}
override fun onActive() {
networkCallback = createNetworkCallback()
val networkRequest = NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
CoroutineScope(Dispatchers.IO).launch {
val activeNetwork = connectivityManager.activeNetwork
if (activeNetwork == null) {
postValue(false)
} else {
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
val hasInternetCapability = networkCapabilities?.hasCapability(NET_CAPABILITY_INTERNET)
if (hasInternetCapability == true) {
val hasInternet = DoesNetworkHaveInternet.execute(activeNetwork.socketFactory)
postValue(hasInternet)
}else {
postValue(false)
}
}
}
}
override fun onInactive() {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
val hasInternetCapability = networkCapabilities?.hasCapability(NET_CAPABILITY_INTERNET)
if (hasInternetCapability == true) {
// Check if this network actually has internet
CoroutineScope(Dispatchers.IO).launch {
val hasInternet = DoesNetworkHaveInternet.execute(network.socketFactory)
if (hasInternet) {
withContext(Dispatchers.Main) {
validNetworks.add(network)
checkValidNetworks()
}
}
}
}
}
override fun onLost(network: Network) {
validNetworks.remove(network)
checkValidNetworks()
}
}
object DoesNetworkHaveInternet {
fun execute(socketFactory: SocketFactory): Boolean {
// Make sure to execute this on a background thread.
return try {
val socket = socketFactory.createSocket() ?: throw IOException("Socket is null.")
socket.connect(InetSocketAddress("8.8.8.8", 53), 1500)
socket.close()
true
} catch (e: IOException) {
false
}
}
}
}
en la actividad declarar el observador
private fun initObservers() {
val netStatusView = binding.contentMain.networkStatus
val connectionLiveData: ConnectionLiveData = ConnectionLiveData(this)
connectionLiveData.observe(this) { isNetworkAvailable ->
Log.d(TAG, "initObservers() called with: isNetworkAvailable = $isNetworkAvailable")
if (isNetworkAvailable) {
Toast.makeText(this@MainActivity, "Connected", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, "Lost", Toast.LENGTH_SHORT).show()
}
}
}
}