一、 序言
之前公司项目采用了声波通讯,但是对于封闭的金属外壳的智能设备来说效果还是不太理想,毕竟金属对于声波的屏蔽还是比较厉害,而且采集声孔很小;经过一方研讨,决定使用Wifi Socket通讯;总结,对于使用一项新技术而言,必不可少的是要进行可行性研究(PS有学过项目工程管理的就知道,感觉我们的产品经理还有很多不足的地方zzzzz)
二、 WIFI配网原理与流程
Demo https://github.com/wudiplk/WifiAndSocket
三、wifi处理
1.wifi开关
wifi相关操作都是用到了WifiManager这个服务,里面包含了wifi基本信息,操作等,还用到了ConnectivityManager这个服判断,主要Android 6.0以上对于wifi状态有了改变。
相关资料:Android动态权限管理https://blog.csdn.net/yanzhenjie1003/article/details/52503533
wifi工具类(PS我只用的做了些改动)
http://www.cnblogs.com/pied/p/3767336.html
package home.gz.com.wifiandsocket;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.util.Log;
import java.util.List;
/**
* @author Wudi
* @date 2018/11/1
*/
public class WifiUtil {
private String TAG = "WifiUtil";
/**
* 定义一个WifiLock
*/
private WifiManager.WifiLock mWifiLock;
public enum Type {
/**
* WiFi加密的几种方式
*/
WPA, WEP, NONE
}
private WifiManager wifiManager;
private ConnectivityManager connectivityManager;
public WifiUtil(Context context) {
wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
connectivityManager= (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
}
/**
* 判断WIFI连接状态
* @return
*/
public boolean isConnect(){
boolean isWifiConnect=false;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// 获取Wifi网络信息
NetworkInfo wifiNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
// 获取移动网络信息
NetworkInfo mobileNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (wifiNetworkInfo != null) {
isWifiConnect = wifiNetworkInfo.isConnected();
}
if (mobileNetworkInfo != null) {
isWifiConnect = mobileNetworkInfo.isConnected();
}
} else {
// 获取所有的网络连接信息
Network[] networks = connectivityManager.getAllNetworks();
if (networks != null) {
for (int j = 0; j < networks.length; j++) {
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(networks[j]);
if (networkInfo != null) {
isWifiConnect = networkInfo.getTypeName().equals("WIFI") && networkInfo.isConnected();
}
}
}
}
return isWifiConnect;
}
/**
* 打开WIFI
*
* @return
*/
public boolean openWifi() {
boolean bRet = true;
if (!wifiManager.isWifiEnabled()) {
bRet = wifiManager.setWifiEnabled(true);
}
return bRet;
}
/**
* 关闭WIFI
*/
public void closeWifi() {
if (wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(false);
}
}
/**
* 无配置记录链接方式
*/
public void connectWithoutConfig(String ssid, String password) {
//打开wifi
if (!openWifi()) {
wifiManager.setWifiEnabled(true);
}
// 等到wifi状态变成WIFI_STATE_ENABLED的时候才能执行下面的语句
while (wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING) {
Log.d(TAG, "正在连接wifi....");
try {
// 为了避免程序一直while循环,让它睡个100毫秒在检测……
Thread.currentThread();
Thread.sleep(100);
} catch (InterruptedException ie) {
}
}
//判断是否已配置过当前热点
WifiConfiguration config = createWifiInfo(ssid, password, Type.WPA);
int netId = wifiManager.addNetwork(config);
if (netId == -1) {
Log.d(TAG, "wifi连接操作失败成功");
}
boolean bRet = wifiManager.enableNetwork(netId, true);
if (bRet) {
Log.d(TAG, "wifi连接成功!");
} else {
Log.d(TAG, "wifi连接失败!");
}
}
/**
* 通过netid 移除wifi配置
*
* @param netId
* @return
*/
public boolean removeWifi(int netId) {
return wifiManager.removeNetwork(netId);
}
/**
* 通过名称移除wifi配置
*
* @param SSID
* @return
*/
public boolean removeWifi(String SSID) {
if (isConfig(SSID) != null) {
return removeWifi(isConfig(SSID).networkId);
} else {
return false;
}
}
/**
* 是否有配置
*
* @param ssid
* @return
*/
public WifiConfiguration isConfig(String ssid) {
List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
if (existingConfigs != null && existingConfigs.size() > 0) {
for (WifiConfiguration existingConfig : existingConfigs) {
if (existingConfig.SSID.equals("\"" + ssid + "\"")) {
return existingConfig;
}
}
}
return null;
}
/**
* @param ssid
* @param password
* @return
*/
public WifiConfiguration createWifiInfo(String ssid, String password, Type type) {
// 如果有相同配置的,就先删除
WifiConfiguration oldWifiConfiguration = isConfig(ssid);
if (oldWifiConfiguration != null) {
wifiManager.removeNetwork(oldWifiConfiguration.networkId);
}
// 添加新配置
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + ssid + "\"";
if (type == Type.NONE) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else if (type == Type.WEP) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
} else if (type == Type.WPA) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
}
return config;
}
/**
* 获取热点的加密类型
*/
public Type getWifiType(ScanResult scanResult) {
if (scanResult.capabilities.contains("WPA")) {
return Type.WPA;
} else if (scanResult.capabilities.contains("WEP")) {
return Type.WEP;
} else {
return Type.NONE;
}
}
/**
* 检查当前WIFI状态
*
* @return
*/
public int checkState() {
return wifiManager.getWifiState();
}
/**
* 锁定WifiLock
*/
public void acquireWifiLock() {
mWifiLock.acquire();
}
/**
* 解锁WifiLock
*/
public void releaseWifiLock() {
// 判断时候锁定
if (mWifiLock.isHeld()) {
mWifiLock.acquire();
}
}
/**
* 创建一个WifiLock
*/
public void creatWifiLock() {
mWifiLock = wifiManager.createWifiLock(TAG);
}
/**
* 得到MAC地址
*
* @return
*/
public String getMacAddress() {
return (wifiManager.getConnectionInfo() == null) ? "NULL" : wifiManager.getConnectionInfo().getMacAddress();
}
/**
* 得到接入点的BSSID
*
* @return
*/
public String getBSSID() {
return (wifiManager.getConnectionInfo() == null) ? "NULL" : wifiManager.getConnectionInfo().getBSSID();
}
/**
* 得到当前IP地址
*
* @return
*/
public String getIpAddress() {
String ip = "";
if (wifiManager.getDhcpInfo() != null) {
int i = wifiManager.getDhcpInfo().ipAddress;
ip = (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
+ "." + (i >> 24 & 0xFF);
}
return ip;
}
public String getServerIpAddress() {
String hostIp = "";
if (wifiManager != null) {
int i = wifiManager.getDhcpInfo().serverAddress;
hostIp = (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
+ "." + (i >> 24 & 0xFF);
wifiManager.getDhcpInfo();
}
return hostIp;
}
/**
* 得到连接的ID
*
* @return
*/
public int getNetworkId() {
return (wifiManager.getConnectionInfo() == null) ? 0 : wifiManager.getConnectionInfo().getNetworkId();
}
/**
* 得到WifiInfo的所有信息包
*
* @return
*/
public String getWifiInfo() {
return (wifiManager.getConnectionInfo() == null) ? "NULL" : wifiManager.getConnectionInfo().toString();
}
}
2.wifi 自动连接
这里主要说明的是连接这部分,自动连接的话开个countTimer计时器就可以了;
/**
* @param ssid
* @param password
* @return
*/
public WifiConfiguration createWifiInfo(String ssid, String password, Type type) {
// 如果有相同配置的,就先删除
WifiConfiguration oldWifiConfiguration = isConfig(ssid);
if (oldWifiConfiguration != null) {
wifiManager.removeNetwork(oldWifiConfiguration.networkId);
}
// 添加新配置
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + ssid + "\"";
if (type == Type.NONE) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
} else if (type == Type.WEP) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
} else if (type == Type.WPA) {
config.preSharedKey = "\"" + password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
}
return config;
}
分别有三种连接类型的WEP,WPA,NONE;一般我们选用WPA,有些手机上有WPA2但都是向下兼容。
连接的时候我们还是要判断下以前是否有链接,我们这里就不再细分了,如果有连接过清楚,重新配置,这样就能避免一些WiFi密码更改,不正确等问题;
3.wifi开启热点模式
在Android 7.0以下 可以通过反射的方式(注意动态权限)
<uses-permission android:name="android.permission.INTERNET" />
<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" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
public void setWifiApEnabledForAndroid_O(){
ConnectivityManager connManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Field iConnMgrField;
try{
iConnMgrField = connManager.getClass().getDeclaredField("mService");
iConnMgrField.setAccessible(true);
Object iConnMgr = iConnMgrField.get(connManager);
Class<?> iConnMgrClass = Class.forName(iConnMgr.getClass().getName());
Method startTethering = iConnMgrClass.getMethod("startTethering",int.class,ResultReceiver.class,boolean.class);
startTethering.invoke(iConnMgr,0,null,true);
Toast.makeText(getApplicationContext(),"热点创建成功",Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
}
}
private void closeWifiHotspot_O(){
ConnectivityManager connManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Field iConnMgrField;
try{
iConnMgrField = connManager.getClass().getDeclaredField("mService");
iConnMgrField.setAccessible(true);
Object iConnMgr = iConnMgrField.get(connManager);
Class<?> iConnMgrClass = Class.forName(iConnMgr.getClass().getName());
Method stopTethering = iConnMgrClass.getMethod("stopTethering",int.class);
stopTethering.invoke(iConnMgr,0);
Toast.makeText(getApplicationContext(),"热点关闭成功",Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
}
}
在Android 8.0以及8.0以上谷歌给这些方法加入隐藏属性,不能通过反射获取,只能另辟蹊径
参考这篇文章
https://blog.csdn.net/VNanyesheshou/article/details/82147110
感觉好像Android 9.0也是有些问题,这个自行解决