【问题标题】:Make Android simultaneously use WiFi to talk to a device and mobile data to talk to a server?让 Android 同时使用 WiFi 与设备通信和移动数据与服务器通信?
【发布时间】:2014-06-26 11:21:46
【问题描述】:

我正在开发一个通过 Wifi 连接到 OBD2 设备的 Android 应用程序,该应用程序可以读取速度、RPM、发动机冷却液温度详细信息等。所以 wifi 仅用于连接 OBD2 设备(它没有设施连接互联网,仅用于与本地客户端通信)。我还需要网络服务的互联网连接。但是在连接我的 wifi 后,我无法通过我在 android 中的移动数据网络连接互联网。

类似的应用程序也是为 iOS 开发的。在 iOS 中,我可以通过 Wifi(静态 Wifi 设置)和蜂窝网络的 Internet 连接使用设备。这意味着使用一些静态 IP 配置我的 wifi,我可以在 iOS 中使用移动数据网络进行 Internet 连接。

但在Android中,如果我使用静态wifi并检查互联网连接,它不可用。

如何通过在 android 中配置 wifi 设置来同时使用 Wifi 和 Internet 连接或以任何其他方式运行?

【问题讨论】:

  • 这里的部分问题是您的系统架构不好 - 加密狗应该通过 BLE 之类的东西进行通信,而那些确实可用的东西。
  • @MidasLefko:您需要查看添加到ConnectivityManagerNetworksuch as openConnection() 的多路径内容。请注意,使用多个进程似乎更简单(例如,bindProcessToNetwork()),但您似乎更愿意避免使用该解决方案。
  • @CommonsWare,multiProcess 是最后的手段,因为连接到 wifi 网络的进程需要向其他进程发送许多频繁的消息,这需要复杂的 IPC 实施。

标签: android wifi obd-ii


【解决方案1】:

首先,我们在这里可能面临的问题是,由于 WiFi 网络上没有互联网连接,HTTP 数据不会通过该连接。解决方法见Send request over WiFi (without connection) even if Mobile data is ON (with connection) on Android M

但是,我遇到了有时没有 HTTP 请求成功的问题。为了解决这个问题,我们可以使用ConnectivityManager.requestNetwork()Network.openConnection()来实现。

确保已启用移动数据和 WiFi 网络并且 Android Manifest 已正确连接:

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

变量:

private ConnectivityManager.NetworkCallback mWifiNetworkCallback, mMobileNetworkCallback;
private Network mWifiNetwork, mMobileNetwork;

获取连接管理器:

final ConnectivityManager manager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);

构建网络回调:

if(mWifiNetworkCallback == null){
    //Init only once
    mWifiNetworkCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(final Network network) {
            try {
                //Save this network for later use
                mWifiNetwork = network;
            } catch (NullPointerException npe) {
                npe.printStackTrace();
            }
        }
    };
}

if(mMobileNetworkCallback == null){
    //Init only once
    mMobileNetworkCallback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(final Network network) {
            try {
                //Save this network for later use
                mMobileNetwork = network;
            } catch (NullPointerException npe) {
                npe.printStackTrace();
            }
        }
    };
}

请求网络:

NetworkRequest.Builder wifiBuilder;
wifiBuilder = new NetworkRequest.Builder();
//set the transport type do WIFI
wifiBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
manager.requestNetwork(wifiBuilder.build(), mWifiNetworkCallback);

NetworkRequest.Builder mobileNwBuilder;
mobileNwBuilder = new NetworkRequest.Builder();
//set the transport type do Cellular
mobileNwBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
manager.requestNetwork(mobileNwBuilder.build(), mMobileNetworkCallback);

像这样提出适当的请求:

public void makeHTTPRequest(final String httpUrl, final String payloadJson, final int timeout,
                          final boolean hasHeaders, final String header1, final String header2) {

    try {
        URL url = new URL(httpUrl);
        HttpURLConnection conn = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            conn = (HttpURLConnection) mWifiNetwork.openConnection(url);

            //Or use mMobileNetwork, if and when required
            //conn = (HttpURLConnection) mMobileNetwork.openConnection(url);
        } else {
            conn = (HttpURLConnection) url.openConnection();
        }
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setReadTimeout(timeout * 1000);
        conn.setConnectTimeout(timeout * 1000);

        conn.setDoInput(true);
        conn.setDoOutput(true);

        if(hasHeaders){
            conn.setRequestProperty("header1", header1);
            conn.setRequestProperty("header2", header2);

        }
        conn.setRequestMethod("PUT");

        OutputStream os = conn.getOutputStream();
        os.write(payloadJson.getBytes());
        os.close();

        final int responseCode = conn.getResponseCode();

        if (responseCode == HttpURLConnection.HTTP_OK) {
            final String statusMessage = conn.getResponseMessage();
            //Log this
        } 
    } catch (SocketException se){
        se.printStackTrace();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

注意: 这些功能可从 Android Lollipop 及更高版本中获得。所以,有必要在合适的地方使用Build.Version.SDK_INT,像这样:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

【讨论】:

    【解决方案2】:
    connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
    

    您可以请求某个主机地址,它必须使用该类型的连接。 如果您使用 Hipri,那么它将占用移动网络。 但这可能会失败!如果可行,则到该地址的所有连接都将通过该类型的连接。

    您可能必须先激活它。

    int resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
    

    这可能需要几秒钟,因为硬件模块必须启动。

    我已经在几个项目中使用了它并且效果很好。 在 2.2 等旧设备上,它的反应真的很不稳定! 但是我在4.0+上没有发现任何问题

    【讨论】:

    • 我可以在您的解决方案中同时使用 wifi 插座和移动网络吗?实际上我的要求是同时使用两者...
    • 是的,你可以!我有同样的要求。只是不要忘记更改路线。如果您更改谷歌分析路由,人们不会喜欢它。这样,每次分析调用都将通过数据网络。请求完成后,wifi旁边的3g图标会在大约10秒后消失。
    • developer.android.com/sdk/api_diff/21/changes/… 这两种方法现在都已弃用...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多