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

当前回答

以下是来自我的Utils类的代码:

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

其他回答

在我目前所见过的所有方法中,最短、最干净的方法应该是:

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

PS:这不会ping任何主机,它只是检查连接状态,所以如果你的路由器没有互联网连接,而你的设备连接到它,这个方法将返回true,尽管你没有互联网。 对于实际的测试,我建议执行一个HttpHead请求(例如到www.google.com)并检查状态,如果它是200 OK,一切正常,并且您的设备有互联网连接。

如果您想在连接丢失时显示警报。你可以使用下面的方法。

此方法用于一次检查连接。首先,你必须在你的类中创建这个。

private boolean isNetworkConnected() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        if(!(cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected())){
            showNetworkDialog();
            return false;
        }
        return true;
    }

在类中创建此方法作为连接侦听器。

private void ConnectionCheck(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkRequest networkRequest = new NetworkRequest.Builder().build();
            connectivityManager.registerNetworkCallback(networkRequest, new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    super.onAvailable(network);
                    Log.i("Tag", "active connection");
                }

                @Override
                public void onLost(Network network) {
                    super.onLost(network);
                    Log.i("Tag", "losing active connection");
                    isNetworkConnected();
                }
            });
        }
    }

为了显示对话框,你可以创建showDialog()方法。

private void showNetworkDialog(){
    new AlertDialog.Builder(MainActivity.this)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setTitle("Connection lost?")
            .setMessage("Please check your internet connection!")
            .setCancelable(false)
            .setPositiveButton("Exit", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .setNegativeButton("Retry", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    isNetworkConnected();
                }
            })
            .show();
}

最后你可以在onCreate()方法中调用这个方法。

if(isNetworkConnected()){
            ConnectionCheck();
        }

在清单文件中,你必须提到许可。

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

Kotlin和协程

我将函数放置在一个ViewModel中,该ViewModel具有viewModelScope。使用一个可观察的LiveData,我通知一个活动有关连接。

ViewModel

 fun checkInternetConnection(timeoutMs: Int) {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                val socket = Socket()
                val socketAddress = InetSocketAddress("8.8.8.8", 53)

                socket.connect(socketAddress, timeoutMs)
                socket.close()

                _connection.postValue(true)
            }
            catch(ex: IOException) {
                _connection.postValue(false)
            }
        }
    }
 private val _connection = MutableLiveData<Boolean>()
 val connection: LiveData<Boolean> = _connection

活动

 private fun checkInternetConnection() {
     viewModel.connection.observe(this) { hasInternet ->
         if(!hasInternet) {
             //hasn't connection
         }
         else {
            //has connection
         }
     }
  }

Jetpack组成/芬兰湾的科特林

根据Levite的回答,我们可以在Jetpack Compose中使用这个组合:

val DNS_SERVERS = listOf("8.8.8.8", "1.1.1.1", "4.2.2.4")
const val INTERNET_CHECK_DELAY = 3000L
@Composable
fun InternetAwareComposable(
    dnsServers: List<String> = DNS_SERVERS,
    delay: Long = INTERNET_CHECK_DELAY,
    successContent: (@Composable () -> Unit)? = null,
    errorContent: (@Composable () -> Unit)? = null,
    onlineChanged: ((Boolean) -> Unit)? = null
) {
    suspend fun dnsAccessible(
        dnsServer: String
    ) = try {
        withContext(Dispatchers.IO) {
            Runtime.getRuntime().exec("/system/bin/ping -c 1 $dnsServer").waitFor()
        } == 0
    } catch (e: Exception) {
        false
    }

    var isOnline by remember { mutableStateOf(false) }
    LaunchedEffect(Unit) {
        while (true) {
            isOnline = dnsServers.any { dnsAccessible(it) }
            onlineChanged?.invoke(isOnline)
            delay(delay)
        }
    }
    if (isOnline) successContent?.invoke()
    else errorContent?.invoke()
}

这个线程中的大多数答案只检查是否有可用的连接,但不检查该连接是否工作,其他答案不是设备范围,我的解决方案应该在每个设备上工作。

你可以在启动应用程序之前在你的主要活动中删除我的代码,它会快速确定是否有实际的互联网连接,如果有对话框将立即删除,应用程序将被启动,如果没有警报会弹出说应用程序需要互联网连接才能工作。

final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
        alertDialog.setTitle("Checking Connection");
        alertDialog.setMessage("Checking...");
        alertDialog.show();
        new CountDownTimer(5000, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {

                new Thread(new Runnable() {
                    public void run() {
                        try {
                            URL url = new URL("http://web.mit.edu/");
                            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            connection.setConnectTimeout(5000);
                            isConnected = connection.getResponseCode() == HttpURLConnection.HTTP_OK;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

                if (isConnected == false){
                    alertDialog.setMessage("Try " +  (5 - millisUntilFinished/1000) + " of 5.");
                } else {
                    alertDialog.dismiss();
                }
            }
            @Override
            public void onFinish() {
                if (isConnected == false) {
                    alertDialog.dismiss();
                    new AlertDialog.Builder(activity)
                            .setTitle("No Internet")
                            .setMessage("Please connect to Internet first.")
                            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    // kill the app?
                                }
                            })
                            .setIcon(android.R.drawable.ic_dialog_alert)
                            .show();
                } else {
                    // Launch the app
                }
            }
        }.start();