我有一个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();
}
}
如果设备处于飞行模式(或者大概在没有可用网络的其他情况下),则cm.getActiveNetworkInfo()
将为null
,因此您需要添加一个null
检查。
修改如下( 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
可能更好。我想这取决于单个用例。
isConnectedOrConnecting()
(用于大多数答案)检查任何网络连接// ICMP
public boolean isOnline() {
Runtime runtime = Runtime.getRuntime();
try {
Process ipProcess = runtime.exec("/system/bin/ping -c 1 8.8.8.8");
int exitValue = ipProcess.waitFor();
return (exitValue == 0);
}
catch (IOException e) { e.printStackTrace(); }
catch (InterruptedException e) { e.printStackTrace(); }
return false;
}
+
可以在主线程上运行
-
在某些旧设备(Galays S3 等)上不起作用,如果没有可用的互联网,它会阻塞一段时间。
// TCP/HTTP/DNS (depending on the port, 53=DNS, 80=HTTP, etc.)
public boolean isOnline() {
try {
int timeoutMs = 1500;
Socket sock = new Socket();
SocketAddress sockaddr = new InetSocketAddress("8.8.8.8", 53);
sock.connect(sockaddr, timeoutMs);
sock.close();
return true;
} catch (IOException e) { return false; }
}
+
非常快(无论哪种方式),都可以在所有设备上使用,非常可靠
-
无法在 UI 线程上运行
这在每个设备上都非常可靠地工作,并且速度非常快。但是,它需要在单独的任务中运行(例如ScheduledExecutorService
或AsyncTask
)。
真的足够快吗?
是的,非常快;-)
除了在互联网上进行测试以外,没有可靠的方法来检查互联网吗?
据我所知,但请告诉我,我将编辑答案。
如果 DNS 关闭,该怎么办?
Google DNS(例如8.8.8.8
)是世界上最大的公共 DNS。截至 2018 年,它每天处理超过一万亿个查询 [ 1 ]。只是说,您的应用可能不会成为今天的话题。
需要哪些权限?
<uses-permission android:name="android.permission.INTERNET" />
只是 Internet 访问 - 令人惊讶 ^^(顺便说一句,您是否曾想过,如果没有此许可,此处建议的某些方法如何甚至可以对 Internet 访问进行远程控制?)
RxJava/RxAndroid
示例(Kotlin)fun hasInternetConnection(): Single<Boolean> {
return Single.fromCallable {
try {
// Connect to Google DNS to check for connection
val timeoutMs = 1500
val socket = Socket()
val socketAddress = InetSocketAddress("8.8.8.8", 53)
socket.connect(socketAddress, timeoutMs)
socket.close()
true
} catch (e: IOException) {
false
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
///////////////////////////////////////////////////////////////////////////////////
// Usage
hasInternetConnection().subscribe { hasInternet -> /* do something */}
RxJava/RxAndroid
示例(Java)public static Single<Boolean> hasInternetConnection() {
return Single.fromCallable(() -> {
try {
// Connect to Google DNS to check for connection
int timeoutMs = 1500;
Socket socket = new Socket();
InetSocketAddress socketAddress = new InetSocketAddress("8.8.8.8", 53);
socket.connect(socketAddress, timeoutMs);
socket.close();
return true;
} catch (IOException e) {
return false;
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
///////////////////////////////////////////////////////////////////////////////////
// Usage
hasInternetConnection().subscribe((hasInternet) -> {
if(hasInternet) {
}else {
}
});
AsyncTask
示例警告:这显示了如何执行请求的另一个示例。但是,由于AsyncTask
,因此应将其替换为应用程序的线程调度,Kotlin Coroutines,Rx,...
class InternetCheck extends AsyncTask<Void,Void,Boolean> {
private Consumer mConsumer;
public interface Consumer { void accept(Boolean internet); }
public InternetCheck(Consumer consumer) { mConsumer = consumer; execute(); }
@Override protected Boolean doInBackground(Void... voids) { try {
Socket sock = new Socket();
sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
sock.close();
return true;
} catch (IOException e) { return false; } }
@Override protected void onPostExecute(Boolean internet) { mConsumer.accept(internet); }
}
///////////////////////////////////////////////////////////////////////////////////
// Usage
new InternetCheck(internet -> { /* do something with boolean response */ });
无需复杂。最简单的框架方式是使用ACCESS_NETWORK_STATE
权限,然后建立一个连接方法
public boolean isOnline() {
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnectedOrConnecting();
}
requestRouteToHost
主机和连接类型(WiFi / 移动设备),也可以使用 requestRouteToHost。
您还需要:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
在您的 Android 清单中。