You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WiFi超时后重定向OkHttp3请求至移动网络遇超时问题求助

解决OkHttp绑定指定Network后出现SocketTimeoutException的问题

看起来你已经做了Network绑定的核心工作,但还是遇到了超时问题,我来分享几个排查方向和修复方案:

1. 先确认Network对象的有效性

你获取的TRANSPORT_CELLULAR类型的Network对象,可能只是系统识别到的移动网络,但未必真的连通了互联网。可以先加个校验:

ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    NetworkCapabilities capabilities = cm.getNetworkCapabilities(mNetwork);
    if (capabilities == null || !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
        Log.e("NetworkCheck", "移动网络无互联网访问权限");
        // 这里可以处理网络不可用的情况,比如放弃绑定
        return;
    }
} else {
    // 低版本用NetworkInfo判断
    NetworkInfo networkInfo = cm.getNetworkInfo(mNetwork);
    if (networkInfo == null || !networkInfo.isConnected()) {
        Log.e("NetworkCheck", "移动网络未连接");
        return;
    }
}

如果校验不通过,那超时的根源就是移动网络本身不可用,和OkHttp配置无关。

2. 确保SSLSocketFactory覆盖所有Socket创建方法

你目前只重写了createSocket(Socket socket, String s, int i, boolean flag),但OkHttp在建立连接时可能会调用其他重载的createSocket方法。要确保所有Socket都通过指定Network创建,需要覆盖全部相关重载:

// 补充这些重载方法到你的SSLSocketFactory实现中
@Override
public Socket createSocket(String host, int port) throws IOException {
    if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return mNetwork.getSocketFactory().createSocket(host, port);
    }
    return mFactory.createSocket(host, port);
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
    if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return mNetwork.getSocketFactory().createSocket(host, port, localHost, localPort);
    }
    return mFactory.createSocket(host, port, localHost, localPort);
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return mNetwork.getSocketFactory().createSocket(host, port);
    }
    return mFactory.createSocket(host, port);
}

@Override
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
    if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return mNetwork.getSocketFactory().createSocket(host, port, localHost, localPort);
    }
    return mFactory.createSocket(host, port, localHost, localPort);
}

如果漏掉这些方法,可能部分连接还是会使用默认网络(比如WiFi),导致超时。

3. 检查权限是否齐全

Android 6.0及以上,除了INTERNET,还需要动态申请ACCESS_NETWORK_STATE和CHANGE_NETWORK_STATE权限,否则可能无法正常绑定Network:

  • 在AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
  • 在代码中动态申请(针对API 23+):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    String[] permissions = {Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.CHANGE_NETWORK_STATE};
    if (checkSelfPermission(permissions[0]) != PackageManager.PERMISSION_GRANTED ||
        checkSelfPermission(permissions[1]) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(permissions, 1001);
        // 等待权限申请结果后再执行网络绑定逻辑
        return;
    }
}

4. 测试移动网络本身的连通性

可以绕过OkHttp,直接用Network对象发起一个简单请求,验证移动网络是否能正常访问互联网:

if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    new Thread(() -> {
        try {
            URL url = new URL("https://www.google.com");
            HttpURLConnection conn = (HttpURLConnection) mNetwork.openConnection(url);
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            int responseCode = conn.getResponseCode();
            Log.d("NetworkTest", "移动网络请求响应码:" + responseCode);
        } catch (Exception e) {
            Log.e("NetworkTest", "移动网络请求失败:" + e.getMessage());
        }
    }).start();
}

如果这个请求也超时,那问题出在移动网络本身(比如APN配置错误、运营商限制、后台数据被禁用等),需要先修复网络问题。

5. 排查DNS解析问题

你的DNS实现看起来没问题,但可以临时改用公共DNS测试,排除移动网络DNS故障的可能:

@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
    if (mNetwork != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // 用谷歌公共DNS 8.8.8.8测试
        InetAddress dnsServer = InetAddress.getByName("8.8.8.8");
        List<InetAddress> addresses = Arrays.asList(mNetwork.getAllByName(hostname, new InetAddress[]{dnsServer}));
        Log.i("NetworkDns", "解析结果:" + addresses);
        return addresses;
    }
    return SYSTEM.lookup(hostname);
}

如果改用公共DNS后请求成功,说明你当前的移动网络DNS解析存在问题,需要调整DNS配置。

6. 禁用OkHttp连接复用

OkHttp默认会复用连接池中的连接,可能导致旧的WiFi连接被复用,干扰新的Network绑定。可以临时禁用连接复用测试:

OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
        .connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
        .readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
        .sslSocketFactory(mSSLSocketFactory, new MyTrustManager())
        .dns(mNetworkDns)
        .hostnameVerifier(new MyHostnameVerifier())
        .connectionPool(new ConnectionPool(0, 1, TimeUnit.NANOSECONDS)); // 禁用连接复用

如果这样能解决问题,后续可以考虑为绑定Network的OkHttpClient单独创建一个连接池,避免和默认连接池冲突。

内容的提问来源于stack exchange,提问作者Mathieu H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:00:51