分享

通过wifi与设备进行通信(Android)

 DeepReading 2019-05-23

最近leader决定把app与设备之间的通信改为wifi,通过http协议实现设备之间的通信。
相对与之前的蓝牙通信,的确简单不少,但实际编码当中也有坑。现在分享出来,希望能给需要的鞋童以帮助,有啥问题大家也可以讨论一下。

切换手机wifi到指定wifi热点

baidu或者google输入以上内容搜索,会出现很多相关资料,但是点开之后,才发现大多都是一样,那么实用性怎么样,于是我验证了一下。
大致思路是,首先创建WifiConfiguration,按照wifi加密方式分为无密码,有密码(WEP,WPA)。

// 创建 WifiConfiguration
public WifiConfiguration CreateWifiInfo(String ssid, String password, int type) {     
      WifiConfiguration config = new WifiConfiguration();          
      config.SSID = "\"" + ssid + "\"";       
      WifiConfiguration tempConfig = this.IsExsits(ssid);               
      if(tempConfig != null) {      
          mWifiManager.removeNetwork(tempConfig.networkId);      
      }    
      if(Type == 1) //WIFICIPHER_NOPASS {     
            此处省略……
      }     
      if(Type == 2) //WIFICIPHER_WEP {  
            此处省略……      
      }     
      if(Type == 3) //WIFICIPHER_WPA {   
            此处省略……       
      }    
       return config;     
} 

从代码中看,之中还判断ssid是否存在,如果存在就用removeNetwork将此ssidwifi从已配置信息wifi列表中remove掉。这一步是必要的,因为ssid就是手机wifi列表中wifi的名称。具有相同ssidwifi可能并不是同一wifi,如果使用了上次保留的配置信息,就可能到导致自动连接wifi失败。但此代码因为是很早之前写的,所以在android6.0版本上并不适用。android6.0新特性加强了对手机权限控制,同时在wifi模块也不再允许对已保存的wifi配置列表进行更新和删除,这将会导致removeNetwork操作失败。

下面看一下添加切换手机到指定wifi热点的代码

// 更改前写法
public boolean addNetwork(WifiConfiguration wcg) {     
     int wcgID = mWifiManager.addNetwork(wcg);     
     boolean b =  mWifiManager.enableNetwork(wcgID, true);     
     return b;  
}

此代码的确能使部分手机成功切换到指定wifi,但其实代码并不规范,这将会导致在部分手机中切换失败。下面介绍正确写法

//更改后写法
public boolean addNetwork(WifiConfiguration wifiConfiguration) {   
    mWifiManager.disconnect();
    int networkId = mWifiManager.addNetwork(wifiConfiguration);
    boolean res = mWifiManager.enableNetwork(networkId, true);
    mWifiManager.saveConfiguration();
    mWifiManager.reconnect();
    return res;
}

指定通过wifi进行http请求

不要以为这样就完了,还有个大坑在等我们

在测试过程中,突然发现,在手机wifi和数据流量同时存在时,部分手机会直接使用数据流量进行通信,这样就导致手机与设备之间无法通信,因为手机与设备只有处在同一局域网下才能正常通信。

这可麻烦了,于是到处到解决办法,终于在WifiManager 这个类找到一个方法enableNetwork上面有一大段英文,我们一起看一下。

/**
 * Allow a previously configured network to be associated with. If
 * <code>disableOthers</code> is true, then all other configured
 * networks are disabled, and an attempt to connect to the selected
 * network is initiated. This may result in the asynchronous delivery
 * of state change events.
 * <p>
 * <b>Note:</b> If an application's target SDK version is
 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network
 * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may
 * instead be sent through another network, such as cellular data,
 * Bluetooth tethering, or Ethernet. For example, traffic will never use a
 * Wi-Fi network that does not provide Internet access (e.g. a wireless
 * printer), if another network that does offer Internet access (e.g.
 * cellular data) is available. Applications that need to ensure that their
 * network traffic uses Wi-Fi should use APIs such as
 * {@link Network#bindSocket(java.net.Socket)},
 * {@link Network#openConnection(java.net.URL)}, or
 * {@link ConnectivityManager#bindProcessToNetwork} to do so.
 *
 * @param netId the ID of the network in the list of configured networks
 * @param disableOthers if true, disable all other networks. The way to
 * select a particular network to connect to is specify {@code true}
 * for this parameter.
 * @return {@code true} if the operation succeeded
 */

从第七行开始,大概意思就是,在应用目标版本大于或等于LOLLIPOP(5.0) 就算wifi是已连接的,网络通信也可能不用wifi,比喻说蜂窝数据。当 wifi 与蜂窝数据同时存在时,当wifi无法使用时,系统会自动切换到蜂窝数据。这不就是我们出现的问题吗,下面赶紧找解决办法。接着看,app确保使用wifi进行通信,应该使用下面三个方法 APIbindSocketopenConnectionbindProcessToNetwork。这个是不是说的有点抽象,就说三个方法,不告诉到底怎么样。额,我们只有迅速补脑了。

    ConnectivityManager mConnectivityManager = (ConnectivityManager)Context.getSystemService(Context.CONNECTIVITY_SERVICE);
    final NetworkRequest networkRequest = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build();
    mConnectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback(){
        @Override
        public void onAvailable(Network network) {
            String url = "";
            try {
                HttpURLConnection conn = (HttpURLConnection) network.openConnection(new URL(url));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });

原来解决办法在 ConnectivityManager 这个类的方法 requestNetwork 可以指定使用 wifi 或者蜂窝数据等访问网络。如果要指定用蜂窝数据进行通信,将 addTransportType 设置为TRANSPORT_CELLULAR即可。在有可用指定传输类型连接上后,onAvailable方法就会调用,其实主要就是获取到 NetworkNetwork 通过 openConnection 得到 HttpURLConnection ,相信大家对HttpURLConnection十分熟悉,直接用它发起网络请求就可以了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多