最近遇到一个问题,在APP发起HTTPS请求之前,想判断一下手机网络是否已连通。查了一轮资料后,发现以前用的好几种方式(这里暂时省略)都过时和失效,特别是在Android10之后,所以萌生了写这个工具类的想法。
这里有几个要求:
- 任何一个DNS连通后,无须等待其他DNS是否连通,马上回调;
- 所有DNS都无法连通,才能认为是检测失败,所以在此过程中即使任何一个DNS连接出错,也不能打断其他DNS的连接请求和回调;
- 需要并发请求,即几个请求同时发出,而不能为串行请求(一个等一个)
- 取消订阅后,抛出的异常无法捕获,将导致程序崩溃,需要在Application设置RxJavaPlugin的ErrorHandler,以接收这类异常
- 取消订阅后,不要再发射事件
所以,考虑使用RxJava2的mergeDelayError操作符和take操作符。
- mergeDelayError:并发去请求,过程出错也不会立即onError打断其他请求
- take:只接受n个事件
使用方法:
首先要在Application的onCreate()中调用
ConnectionCheckUtil.initRxJavaDisposeErrorHandler()
然后,在需要的地方调用:
ConnectionCheckUtil.connectionCheck(object : OnConnectionCheckListener {
override fun onConnected() {
//connected
}
override fun onDisconnected() {
//not connected
}
})
最后附上源码和GitHub Demo:
import com.orhanobut.logger.Logger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
public class ConnectionCheckUtil {
private static final String TAG = "ConnectionCheckUtil";
private static final int timeout = 1000;
private static final String dns1 = "8.8.8.8";
private static final String dns2 = "8.8.4.4";
public interface OnConnectionCheckListener{
void onConnected();
void onDisconnected();
}
public static void connectionCheck(OnConnectionCheckListener callBack) {
Observable check1 = getConnectionCheckObservable(dns1);
Observable check2 = getConnectionCheckObservable(dns2);
Observable.mergeDelayError(check1, check2)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.take(1)
.subscribe(getResultCheckObserver(callBack));
}
private static Observable getConnectionCheckObservable(String ip) {
return Observable.create((ObservableOnSubscribe<String>) e -> {
try {
Socket s;
s = new Socket();
InetAddress host = InetAddress.getByName(ip);
s.connect(new InetSocketAddress(host, 53), timeout);
s.close();
if(!e.isDisposed()) {
Logger.t(TAG).d("onNext" + ip);
e.onNext(ip);
}
} catch (Exception ex) {
if(!e.isDisposed()) {
Logger.t(TAG).d("onError" + ex.getMessage());
e.onError(ex);
}
}
});
}
private static Observer<String> getResultCheckObserver(OnConnectionCheckListener callBack) {
return new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.t(TAG).d("onSubscribe : " + d.isDisposed() + "\n");
}
@Override
public void onNext(@NonNull String ip) {
Logger.t(TAG).d("onNext : value : " + ip + "\n");
}
@Override
public void onError(@NonNull Throwable e) {
Logger.t(TAG).d("onError : value : " + e.getMessage() + "\n");
callBack.onDisconnected();
}
@Override
public void onComplete() {
Logger.t(TAG).d( "onComplete" + "\n");
callBack.onConnected();
}
};
}
public static void initRxJavaDisposeErrorHandler() {
RxJavaPlugins.setErrorHandler(throwable -> {
});
}
}
参考资料:
FATAL EXCEPTION: RxCachedThreadScheduler-4 or -1 or -2 or -3