最近
leader
决定把app
与设备之间的通信改为wifi
,通过http
协议实现设备之间的通信。
相对与之前的蓝牙通信,的确简单不少,但实际编码当中也有坑。现在分享出来,希望能给需要的鞋童以帮助,有啥问题大家也可以讨论一下。
在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
将此ssid
的wifi
从已配置信息wifi
列表中remove
掉。这一步是必要的,因为ssid
就是手机wifi
列表中wifi
的名称。具有相同ssid
的wifi
可能并不是同一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和数据流量同时存在时,部分手机会直接使用数据流量进行通信,这样就导致手机与设备之间无法通信,因为手机与设备只有处在同一局域网下才能正常通信。
这可麻烦了,于是到处到解决办法,终于在WifiManager
这个类找到一个方法enableNetwork
上面有一大段英文,我们一起看一下。
/**
* Allow a previously configured network to be associated with. If
* disableOthers
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.
*
* Note: 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
进行通信,应该使用下面三个方法 APIbindSocket
、openConnection
、bindProcessToNetwork
。这个是不是说的有点抽象,就说三个方法,不告诉到底怎么样。额,我们只有迅速补脑了。
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
方法就会调用,其实主要就是获取到 Network
。 Network
通过 openConnection
得到 HttpURLConnection
,相信大家对HttpURLConnection
十分熟悉,直接用它发起网络请求就可以了。
github 个人地址