【问题标题】:Force a specific network in android even if it doesn't have internet connectivity强制在 android 中使用特定网络,即使它没有互联网连接
【发布时间】:2020-04-10 14:11:49
【问题描述】:

我的项目是使用旧的 USB 网络摄像头和 Raspberry Pi 制作后视摄像头。

我将摄像头插入 pi 并使用“motion”,当我与 Pi 位于同一网络时,我可以使用浏览器 (http://<ip-address>:8081) 访问实时视频。

为此,我将我的 Pi 设置为 wifi 热点,以便与我的手机在同一个网络中访问视频。

但是:在我使用 Android Studio 创建一个应用程序后,使用 WebView 打开视频链接,当启用移动数据时,我会收到一个ERR_CONNECTION_REFUSED。因为如果没有检测到互联网连接,则不会使用 Wifi 网络。

我不想在每次使用时手动禁用它,我希望应用程序使用仅 Wi-Fi

我设法使用此代码自动连接到 pi WiFi:

    WifiNetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
            .setSsid("Wifi-Fourcon")
            .setWpa2Passphrase("1234567890")
            .build();

    final NetworkRequest request = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // we want WiFi
            .setNetworkSpecifier(specifier) // we want _our_ network
            .build();

    ConnectivityManager connectivityManager = (ConnectivityManager)this.getSystemService(CONNECTIVITY_SERVICE);

    connectivityManager.requestNetwork(request,

我尝试添加

.removeTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)

但结果相同。

我还尝试使用

禁用移动数据
setMobileDataEnabled()

但是我们不能再使用这种方法了..

【问题讨论】:

  • 看我的回答,你能确认问题是wifi没有提供互联网连接,因此webview没有通过wifi加载吗?

标签: android android-wifi android-networking


【解决方案1】:

所以我假设这里的问题是 Wifi 连接实际上并没有提供互联网,所以 android 忽略了网络请求的连接。


编辑:阅读完文档后,bindProcessToNetwork 方法似乎更适合用例。它将整个过程限制为仅使用该特定网络。我再次没有对此进行测试,但它应该很容易通过在onAvailable 中调用该方法并在onLost 中使用null 调用它,如果您想在需要时反转绑定。现有的套接字不会被强制,因此最安全的方法是仅在绑定到网络后才初始化 WebView。


可以通过覆盖 WebView 加载其请求的方式并强制它使用您的网络请求的连接来规避此问题。一个完整的解决方案可能看起来像这样,虽然我没有测试过。让我知道这是否有效:

Network networkInstance;

void getNetwork(Context context) {

    final NetworkRequest request = new NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // we want WiFi
            .setNetworkSpecifier("foobar") // we want _our_ network
            .build();


    ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    connectivityManager.requestNetwork(request, new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(@NonNull Network network) {
            networkInstance = network;
            loadWebView();
        }

        @Override
        public void onLost(@NonNull Network network) {
            // Notify the user or something ...
            super.onLost(network);
        }
    });

}


public void setupWebView(WebView webView) {


    webView.setWebViewClient(new WebViewClient() {
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

            if (networkInstance != null) {
                try {
                    URL url = new URL(request.getUrl().toString());
                    HttpURLConnection connection = (HttpURLConnection) networkInstance.openConnection(url);
                    connection.setDoOutput(true);
                    connection.setRequestMethod(request.getMethod());

                    for (Map.Entry<String, String> header : request.getRequestHeaders().entrySet()) {
                        connection.setRequestProperty(header.getKey(), header.getValue());
                    }

                    int responseCode = connection.getResponseCode();
                    String responseMessage = connection.getResponseMessage();
                    String contentType = connection.getContentType();
                    String encoding = connection.getContentEncoding();

                    // Transform response headers from Map<String, List<String>> to Map<String, String>
                    HashMap<String, String> headerFields = new HashMap<>();
                    for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
                        headerFields.put(entry.getKey(), entry.getValue().get(0));
                    }

                    String[] contentTypeData = contentType.split(";\\s?");
                    contentType = contentTypeData[0];
                    if (contentTypeData.length > 1) {
                        encoding = contentTypeData[1];
                    }

                    if (contentType.contains("text") && encoding == null) {
                        encoding = "UTF-8";
                    }

                    InputStream inputStream;
                    try {
                        inputStream = connection.getInputStream();
                    } catch (Exception e) {
                        inputStream = connection.getErrorStream();
                    }

                    return new WebResourceResponse(
                            contentType,
                            encoding,
                            responseCode,
                            responseMessage,
                            headerFields,
                            inputStream
                    );

                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

            return super.shouldInterceptRequest(view, request);
        }
    });
}

setupWebView 应与 WebView 实例一起调用一次以设置 WebViewClient。 (如果您已经在使用 WebViewClient,只需复制覆盖的 shouldInterceptRequest 方法即可。)

【讨论】:

  • 我用一些内容编码修复更新了答案。它适用于基本的东西。 POST 请求不起作用,因为 webView 没有提供 API 来拦截它。
  • 谢谢。我在这里放了一个 kotlin 实现:github.com/darran-kelinske-fivestars/network-switch-poc/blob/… - 一些高级网页只是显示文本而不是渲染页面....
  • @dazza5000 是的,它非常有限。我真的推荐bindProcessToNetwork 方法,但是一旦您需要同时使用移动网络和 wifi 网络,它就会崩溃。
【解决方案2】:

Google 禁用了修改关键 Android 功能的功能,例如启用/禁用移动互联网。

【讨论】:

  • 这就是为什么我希望我的应用使用 Wi-Fi 而不是移动数据,即使它仍然处于启用状态。
  • WI-FI 功能也是一个关键的修改;)。我的回答不够清楚。
  • 我想这个问题还不够清楚,为什么会这样,请参阅我的回答以获取一些信息。
猜你喜欢
  • 2017-07-18
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-02
  • 1970-01-01
相关资源
最近更新 更多