智能设备Socket通讯Wifi配网(一)

一、 序言 之前公司项目采用了声波通讯,但是对于封闭的金属外壳的智能设备来说效果还是不太理想,毕竟金属对于声波的屏蔽还是比较厉害,而且采集声孔很小;经过一方研讨,决定使用Wifi Socket通讯;总结,对于使用一项新技术而言,必不可少的是要进行可行性研究(PS有学过项目工程管理的就知道,感觉我们的产品经理还有很多不足的地方zzzzz)
二、 WIFI配网原理与流程 【智能设备Socket通讯Wifi配网(一)】
智能设备Socket通讯Wifi配网(一)
文章图片
WIFI配网.png
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 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以下 可以通过反射的方式(注意动态权限)

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也是有些问题,这个自行解决

    推荐阅读