我得到了一个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();
    }   
}

当前回答

public boolean isOnline() {
    boolean var = false;
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    if ( cm.getActiveNetworkInfo() != null ) {
        var = true;
    }
    return var;
} 

我是这样做的。 我想会更短更有可读性。

干杯!

Saiyan

其他回答

最简单的解决办法是

在大多数情况下,如果他/她想连接到远程服务器,只会检查互联网连接,所以简单和最好的解决方案是ping你的服务器,如下所示。

public boolean isConnected() {
    final String command = "ping -c 1 yourExmapleDomain.com";
    boolean isConnected = false;
    try {
        isConnected = Runtime.getRuntime().exec(command).waitFor() == 0;
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return isConnected;
}

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

由于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 }

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

如果设备处于飞行模式(或者假设在没有可用网络的其他情况下),cm.getActiveNetworkInfo()将为空,因此您需要添加一个空检查。

修改(Eddie的解决方案)如下:

public boolean isOnline() {
    ConnectivityManager cm =
        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

在AndroidManifest.xml中添加以下权限:

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

还有一点,如果你在给定的时间点绝对需要一个网络连接,那么使用netInfo.isConnected()而不是netInfo.isConnectedOrConnecting可能会更好。不过,我想这取决于个人用例。

使用以下方法检查互联网连接,同时ConnectivityManager.getActiveNetworkInfo()在API 28中已弃用

@Suppress("DEPRECATION")
fun isNetworkConnected(context: Context): Boolean {
    val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
    return cm?.run {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            cm.getNetworkCapabilities(cm.activeNetwork)?.run {
                when {
                    hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
                    hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
                    hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
                    else -> false
                }
            }
        } else {
            cm.activeNetworkInfo?.run {
                when (type) {
                    ConnectivityManager.TYPE_WIFI -> true
                    ConnectivityManager.TYPE_MOBILE -> true
                    else -> false
                }
            }
        }
    } ?: false

}

还要向清单添加以下权限

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

hatransport_ethernet (NetworkCapabilities.TRANSPORT_ETHERNET)用于为Android TV开发的应用程序,其中TV可以直接连接到以太网

如果你正在使用Firebase,你可以使用这个。

Java:

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            Log.d(TAG, "connected");
        } else {
            Log.d(TAG, "not connected");
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled");
    }
});

科特林:

val connectedRef = Firebase.database.getReference(".info/connected")
connectedRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val connected = snapshot.getValue(Boolean::class.java) ?: false
        if (connected) {
            Log.d(TAG, "connected")
        } else {
            Log.d(TAG, "not connected")
        }
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled")
    }
})