我得到了一个AsyncTask,应该检查对主机名的网络访问。但是doInBackground()永远不会超时。有人知道吗?

public class HostAvailabilityTask extends AsyncTask<String, Void, Boolean> {

    private Main main;

    public HostAvailabilityTask(Main main) {
        this.main = main;
    }

    protected Boolean doInBackground(String... params) {
        Main.Log("doInBackground() isHostAvailable():"+params[0]);

        try {
            return InetAddress.getByName(params[0]).isReachable(30); 
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;       
    }

    protected void onPostExecute(Boolean... result) {
        Main.Log("onPostExecute()");

        if(result[0] == false) {
            main.setContentView(R.layout.splash);
            return;
        }

        main.continueAfterHostCheck();
    }   
}

当前回答

我在这里看到了很多过时的答案,所以我决定加入我的答案。

由于Android 10 (API级别29)getActiveNetworkInfo()已弃用,谷歌建议我们使用NetworkCallbacks而不是针对Android 10及更高版本的应用程序。

关于阅读网络状态的文档提供了一些关于如何使用NetworkCallback的信息,但我没有设法找到一个很好的代码示例,整个事情的工作,所以这里是我提出的代码,我们在我们的应用程序中使用:

import android.content.Context
import android.net.ConnectivityManager
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
import com.fieldontrack.kmm.common.network.ConnectivityMonitor
import com.fieldontrack.kmm.entities.connectivity.NetworkType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class ConnectivityMonitorImpl(appContext: Context) : ConnectivityMonitor {
    private val connectivityManager = appContext.getSystemService(ConnectivityManager::class.java)
    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) =
            connectivityManager.getNetworkCapabilities(network)?.let { networkCapabilities ->
                updateConnectionStatus(networkCapabilities = networkCapabilities)
                updateNetworkType(networkCapabilities = networkCapabilities)
            } ?: run {
                _isConnectedState.value = true
            }

        override fun onLost(network: Network) {
            // Do not check for NetworkCapabilities here, as they might be wrong.
            // If we get this callback, we're certain that we've lost connection.
            _isConnectedState.value = false
            _networkTypeState.value = NetworkType.Unknown
        }

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            updateConnectionStatus(networkCapabilities = networkCapabilities)
            updateNetworkType(networkCapabilities = networkCapabilities)
        }

        override fun onLinkPropertiesChanged(
            network: Network,
            linkProperties: LinkProperties
        ) = Unit
    }
    private val _isConnectedState = MutableStateFlow(false)
    private val _networkTypeState = MutableStateFlow(NetworkType.Unknown)

    override val isConnectedState: StateFlow<Boolean> = _isConnectedState
    override val networkTypeState: StateFlow<NetworkType> = _networkTypeState

    override val isConnected: Boolean
        get() = _isConnectedState.value

    override val networkType: NetworkType
        get() = _networkTypeState.value

    init {
        startMonitoring()
    }

    override fun startMonitoring() =
        connectivityManager.registerDefaultNetworkCallback(networkCallback)

    override fun stopMonitoring() =
        connectivityManager.unregisterNetworkCallback(networkCallback)

    private fun updateConnectionStatus(networkCapabilities: NetworkCapabilities) {
        val isConnected =
            networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)

        _isConnectedState.value = isConnected
    }

    private fun updateNetworkType(networkCapabilities: NetworkCapabilities) {
        val networkType = when {
            networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.WiFi
            networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.Cellular
            else -> NetworkType.Unknown
        }

        _networkTypeState.value = networkType
    }
}

ConnectivityMonitor界面非常简单:

interface ConnectivityMonitor {
    val isConnected: Boolean
    val networkType: NetworkType

    val isConnectedState: StateFlow<Boolean>
    val networkTypeState: StateFlow<NetworkType>
    
    fun startMonitoring()
    fun stopMonitoring()
}

NetworkType只是一个简单的枚举:

enum class NetworkType { Unknown, Cellular, WiFi }

据我测试,无论应用程序是在后台还是前台,这都是可行的。

其他回答

芬兰湾的科特林实现

/**
* Function that uses ping, takes server name or ip as argument.
*
* @return [Double.MAX_VALUE] if server is not reachable. Average RTT if the server is reachable.
*
* Success output example
*
* PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
* 64 bytes from 8.8.8.8: icmp_seq=1 ttl=254 time=172 ms
* 64 bytes from 8.8.8.8: icmp_seq=2 ttl=254 time=166 ms
* 64 bytes from 8.8.8.8: icmp_seq=3 ttl=254 time=167 ms
* 64 bytes from 8.8.8.8: icmp_seq=4 ttl=254 time=172 ms
* 64 bytes from 8.8.8.8: icmp_seq=5 ttl=254 time=167 ms

* --- 8.8.8.8 ping statistics ---
* 5 packets transmitted, 5 received, 0% packet loss, time 4011ms
* rtt min/avg/max/mdev = 166.470/169.313/172.322/2.539 ms
*          |________________________|
* value to parse using it.split('=')[1].trim().split(' ')[0].trim().split('/')[1].toDouble()
*/
@ExperimentalStdlibApi
fun pingServerAverageRtt(host: String): Double {

    var aveRtt: Double = Double.MAX_VALUE

    try {
        // execute the command on the environment interface, timeout is set as 0.2 to get response faster.
        val pingProcess: Process = Runtime.getRuntime().exec("/system/bin/ping -i 0.2 -c 5 $host")
        // gets the input stream to get the output of the executed command
        val bufferedReader = BufferedReader(InputStreamReader(pingProcess.inputStream))

        bufferedReader.forEachLine {
            if (it.isNotEmpty() && it.contains("min/avg/max/mdev")) {  // when we get to the last line of executed ping command
                aveRtt = it.split('=')[1].trim()
                        .split(' ')[0].trim()
                        .split('/')[1].toDouble()
            }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }

    return aveRtt
}

使用的例子


val latency = pingServerAverageRtt(ipString)
if (latency != Double.MAX_VALUE) { 
    //server reachable
} else {
    //server not reachable
}

这种方法为您提供了一个非常快速的方法(用于实时反馈)或较慢的方法(用于需要可靠性的一次性检查)。

public boolean isNetworkAvailable(bool SlowButMoreReliable) {
    bool Result = false; 
    try {
        if(SlowButMoreReliable){
            ConnectivityManager MyConnectivityManager = null;
            MyConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

            NetworkInfo MyNetworkInfo = null;
            MyNetworkInfo = MyConnectivityManager.getActiveNetworkInfo();

            Result = MyNetworkInfo != null && MyNetworkInfo.isConnected();

        } else
        {
            Runtime runtime = Runtime.getRuntime();
            Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");

            int i = ipProcess.waitFor();

            Result = i== 0;

        }

    } catch(Exception ex)
    {
        //Common.Exception(ex); //This method is one you should have that displays exceptions in your log
    }
    return Result;
}

下面是我使用的方法:

public boolean isNetworkAvailable(final Context context) {
    return ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo() != null;
}

更好的是,检查确保它是“连接”的:

public boolean isNetworkAvailable(final Context context) {
    final ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
    return connectivityManager.getActiveNetworkInfo() != null && connectivityManager.getActiveNetworkInfo().isConnected();
}

下面是如何使用该方法:

if (isNetworkAvailable(context)) {
    // code here
} else {
    // code
}

需要许可:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

https://stackoverflow.com/a/16124915/950427

可以通过该方法检测网络可用性-

public static boolean isDeviceOnline(Context context) {
        boolean isConnectionAvail = false;
        try {
            ConnectivityManager cm = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo netInfo = cm.getActiveNetworkInfo();
            return netInfo.isConnected();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isConnectionAvail;
    }

从以下链接找到并修改(!):

在你的manifest文件中至少添加:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

如果您正在访问它,您可能已经拥有INTERNET权限。那么一个允许测试连通性的布尔函数是:

private boolean checkInternetConnection() {
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    // test for connection
    if (cm.getActiveNetworkInfo() != null
            && cm.getActiveNetworkInfo().isAvailable()
            && cm.getActiveNetworkInfo().isConnected()) {
        return true;
    } else {
        Log.v(TAG, "Internet Connection Not Present");
        return false;
    }
}