要实时观察连接变化,您可以使用 LiveData。在此之前我们使用了 BroadcastReceiver,但正如您所见 Declaring a broadcastreceiver for android.net.conn.CONNECTIVITY_CHANGE is deprecated for apps targeting N and higher.
我将发布完整的代码并解释它的每一部分。
public class ConnectionLiveData extends LiveData<Boolean> {
private ConnectivityManager.NetworkCallback networkCallback;
private final ConnectivityManager cm;
private final HashSet<Network> validNetworks = new HashSet<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public static final int NETWORK_STATUS_POST_DELAY = 1000;
public ConnectionLiveData(Context context) {
cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
private void checkValidNetworks() {
new Handler(Looper.getMainLooper()).postDelayed(() ->
postValue(validNetworks.size() > 0),
NETWORK_STATUS_POST_DELAY);
}
@Override
protected void onActive() {
networkCallback = createNetworkCallback();
NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
cm.registerNetworkCallback(networkRequest, networkCallback);
}
@Override
protected void onInactive() {
cm.unregisterNetworkCallback(networkCallback);
}
private ConnectivityManager.NetworkCallback createNetworkCallback() {
return new ConnectivityManager.NetworkCallback() {
/*
Called when a network is detected. If that network has internet, save it in the Set.
Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network)
*/
@Override
public void onAvailable(@NonNull Network network) {
NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
boolean hasInternetCapability = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
if (hasInternetCapability) {
// check if this network actually has internet
executor.execute(() -> {
boolean hasInternet = DoesNetworkHaveInternet.execute(network.getSocketFactory());
if (hasInternet) {
validNetworks.add(network);
checkValidNetworks();
}
});
}
}
/*
If the callback was registered with registerNetworkCallback() it will be called for each network which no longer satisfies the criteria of the callback.
Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onLost(android.net.Network)
*/
@Override
public void onLost(@NonNull Network network) {
validNetworks.remove(network);
checkValidNetworks();
}
};
}
}
我们在这里扩展LiveData 并实现两个功能:onActive() 和onInactive()。如您所知,LiveData 是生命周期感知的,当有一个观察者时,onActive 将被调用。我用它们来注册/注销ConnectivityManager。
当我们注册 ConnectivityManager 时,我们将 networkCallback 作为参数传递。它有几种方法,但正如您在 createNetworkCallback() 函数中看到的那样,我们仅覆盖 onAvailable() 和 onLost() 函数。如果您要打开/关闭网络,将调用 onAvailable() 和 onLost()。
可能存在连接打开但与服务器没有连接的情况。在这种情况下,我找到了一个简单的类,我们可以使用它来 ping 和验证我们的连接。
使用ExecutorService 我通过ping 验证互联网连接。
boolean hasInternet = DoesNetworkHaveInternet.execute(network.getSocketFactory());
DoesNetworkHaveInternet 代码:
public class DoesNetworkHaveInternet {
private static final String HOST_NAME = "8.8.8.8";
private static final int PORT = 53;
private static final int CONNECT_TIMEOUT = 1500;
@WorkerThread
static Boolean execute(SocketFactory socketFactory) {
try {
Socket socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(HOST_NAME, PORT), CONNECT_TIMEOUT);
socket.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
它正在 ping 并验证我们有稳定的连接。
最后,在验证我们有连接之后,我们调用checkValidNetworks() 函数。有延迟,因为在某些情况下,您的连接可能会暂时关闭和打开,为此我发现没有必要显示网络错误。 checkValidNetworks() 检查validNetworks 是否不为空。这是一个列表,因为存在某些类型的连接,例如 wifi 和移动网络。如果至少有一个稳定的连接,validNetworks 不会为空。
而已。您可以在您的片段或这样的活动中观察它:
ConnectionLiveDataJava connectionLiveData = new ConnectionLiveDataJava(this);
connectionLiveData.observe(this, isConnected -> {
});