版权声明
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/9708586.html
前言
此篇博客讲解WiFi正常功能开发与热点功能开发,但是请注意,这里的API版本是28,Android9.0。 因为Android10 wifi的调用完全不一样了。
转载请注明来源:https://www.cnblogs.com/guanxinjing/p/9708586.html
权限
不管是WiFi正常功能还是WiFi热点都需要以下权限,Android5.0还需要动态权限
<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_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
WiFi正常功能开发
初始化WiFi管理
操作WiFi的基础类,所有的WiFi操作都需要使用这个管理类
WifiManager mWifiManager = (WifiManager)getApplicationContext().getSystemService(Context.WIFI_SERVICE);
判断WiFi状态
这个是判断设备的WiFi硬件状态,一共有已经几种设备状态:
WIFI_STATE_DISABLED WiFi已经禁用
WIFI_STATE_DISABLING 正在禁用WiFi中
WIFI_STATE_ENABLED WiFi已经启用
WIFI_STATE_ENABLING 正在启用WiFi中
WIFI_STATE_UNKNOWN 设备状态未知(或者没有WiFi硬件支持)
if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED){ Log.e(TAG, "WiFi已经启用"); }
判断WiFi是否开启
这是官方提供的更快捷判断WiFi是否开启的方法
if (mWifiManager.isWifiEnabled()){ Log.e(TAG, "WiFi已经开启"); }else { Log.e(TAG, "WiFi已经关闭"); }
开启或者关闭WiFi
用此方法开启WiFi会有一个系统弹窗提示用户手动开启WiFi.
mWifiManager.setWifiEnabled(true); //false为关闭
搜索WiFi与获取WiFi设备列表
/** * 搜索WiFi */ fun scan() { mWifiManager.isScanAlwaysAvailable mWifiManager.startScan() } /** * 注册搜索结果监听 */ private fun registerReceiver(context: Context) { val wiFiChangeReceiver = WiFiChangeReceiver() val intentFilter = IntentFilter() //搜索Wifi扫描已完成,并且结果可用 intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) context.registerReceiver(wiFiChangeReceiver, intentFilter) } /** * 获取搜索到的设备 */ @SuppressLint("MissingPermission") private suspend fun getScanDevice() { val list: List<ScanResult> = mWifiManager.scanResults //获取WiFi列表 for (scanResult in list) { Log.e("zh", "getScanDevice: ${scanResult}" ) } } inner class WiFiChangeReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { //搜索完成 if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) { GlobalScope.launch(Dispatchers.Main) { val list = withContext(Dispatchers.IO) { getScanDevice() } } } } }
搜索是一个异步的过程,你可以依靠广播回调获取WiFi数据, 也可以立即获取WiFi数据,但是立即获取可能是旧的WiFi数据。
注意一! mWifiManager.getScanResults() 得到的List里面的数据其实是一直会变化的! 意思是计算你获取到了这个list,在WiFi的底层代码里依然会修改/增加/删除这个list里的数据
注意二! mWifiManager.getScanResults() 得到的List里面的数据会出现重复/空名/个别值为null的问题,这是正常的.所以WiFi的难点不是连接与搜索,而是清洗数据
ScanResult可以获取的信息
ScanResult是搜索到WiFi列表后获取的单个WiFi信息,里面包含了一些信息需要了解一下(注意,如果你看源码会发现有很多信息是隐藏的无法调用的.那些变量都是系统级别的API才能调用,这里只说明可以看到的信息)
CharSequence area = scanResult.venueName; //WiFi接入点的地点名称 int frequency = scanResult.frequency; //频率 long refreshTime = scanResult.timestamp; //此WiFi最新的刷新时间戳 int level = scanResult.level; //信号强度 String encryption = scanResult.capabilities;//加密方式 例如:WEP/WAP/WAP2 String address = scanResult.BSSID; //WiFi的地址 String name = scanResult.SSID; //WiFi的名称
连接指定的WIiFi网络(API28)
请注意!此代码在Android10 API29的版本上已经无法使用 在新的API 29的SDK中 enableNetwork() 方法始终会返回false, 如果你非要使用可以考虑将API降到28:
这段流程比较长,大致是这样的:
1.搜索WiFi,找到指定WiFi 获取名称/地址/加密方式,以及你自己知道的WiFi密码
2.创建WiFi配置信息WifiConfiguration, 添加WiFi名称,地址
3.在根据加密方式以对应的方式添加密码到WifiConfiguration
4.将WiFi配置WifiConfiguration,添加到以配置的网络列表里addNetwork(wifiConfiguration);
5.获取已经配置好的网络列表mWifiManager.getConfiguredNetworks();,找到指定WiFi,获取id
6.断开现在正在连接的WiFi,输入id启用设置好的WiFi,重新连接
注意! scanResult.BSSID 这个BSSID其实动态生成的路由地址,不可以依靠这来判断wifi的唯一性. 所以用SSID加BSSID来锁定一个WiFi是不现实的.因为有时候BSSID其实是null的.只能使用SSID来判断WiFi,遇到重名WiFi没有什么办法
/** * 连接WiFi */ fun connect(ssid: String, password: String, capabilities:String) { val configurationList = mWifiManager.configuredNetworks //已经保存密码的WiFi val item = configurationList.find { it.SSID == ssid } if (item != null) { //如果是已经保存密码的的WiFi就重新连接 mWifiManager.enableNetwork(item.networkId, true) } else { val wifiConfiguration = WifiConfiguration() //清除一些默认wifi的配置 wifiConfiguration.allowedAuthAlgorithms.clear() wifiConfiguration.allowedGroupCiphers.clear() wifiConfiguration.allowedKeyManagement.clear() wifiConfiguration.allowedPairwiseCiphers.clear() wifiConfiguration.allowedProtocols.clear() wifiConfiguration.SSID = addQuotationMarks(ssid) // 如果包含WAP-PSK的话,则为WAP加密方式 if (capabilities.contains("WPA-PSK") || capabilities.contains("WPA2-PSK")) { wifiConfiguration.preSharedKey = addQuotationMarks(password) wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN) wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP) wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP) wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP) wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP) wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN) wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.WPA) wifiConfiguration.status = WifiConfiguration.Status.ENABLED } else if (capabilities.contains("WEP")) { wifiConfiguration.wepKeys[0] = addQuotationMarks(password) wifiConfiguration.wepTxKeyIndex = 0; wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40); } else if (capabilities.contains("ESS")) { // 如果是ESS则没有密码 wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE) } else if (capabilities.contains("WPA2-EAP")) { //不支持企业WiFi密码 } val netId = mWifiManager.addNetwork(wifiConfiguration) mWifiManager.enableNetwork(netId, true) } } /** * 添加引号 * * @param content * @return */ private fun addQuotationMarks(content: String): String { return "\"" + content + "\"" }
连接指定的WIiFi网络(API29)
//Android10以上 通过P2P连接Wifi @RequiresApi(Build.VERSION_CODES.Q) private fun connectByP2P(ssid: String, password: String) { val specifier = WifiNetworkSpecifier.Builder() .setSsid(ssid) .setWpa2Passphrase(password) .build() val request = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .setNetworkSpecifier(specifier) .build() val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network?) { // do success processing here.. Toast.makeText(context, context.getString(R.string.connect_success), Toast.LENGTH_SHORT).show() } override fun onUnavailable() { // do failure processing here.. Toast.makeText(context, context.getString(R.string.connect_fail), Toast.LENGTH_SHORT).show() } } connectivityManager.requestNetwork(request, networkCallback) } //Android10以上,通过suggestion连接WIFI private fun connectBySug(ssid: String, password: String) { val suggestion = WifiNetworkSuggestion.Builder() .setSsid(ssid) .setWpa2Passphrase(password) .setIsAppInteractionRequired(true) // Optional (Needs location permission) .build() val suggestionsList = listOf(suggestion) //wifiManager.removeNetworkSuggestions(suggestionsList) val status = wifiManager.addNetworkSuggestions(suggestionsList) Log.d(TAG, status.toString()) if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { } val intentFilter = IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION); val broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) { return } } }; context.registerReceiver(broadcastReceiver, intentFilter); }
获取已经配置密码并且保存的WiFi网络列表
上面的代码里有说明获取已经配置密码的WiFi网络列表的部分代码了,就是如下代码,但是这里在强调一下.他的使用情景与信息
1.WiFi是有暂存的,以记录密码的WiFi系统会保存起来,方便某个WiFi断开后自动重新连接这个配置列表里的其他WiFi
2.所以在如果要删除某个WiFi,你必需移除这个配置列表里的这个WiFi,如果只是断开操作将可能自动重连
3.这个配置列表还能告诉用户已经保存连接过所有WiFi的,用户直接点击后重新连接这个WiFi(不需要再次输入密码)
4.你可以重新更新这个配置列表的WiFi密码
注意! 跟上面的一样 mWifiManager.getConfiguredNetworks() 得到的List里面的数据会出现重复/空名/个别值为null的问题.另外这里获取的WiFi数据是不含信号强度的.如果你需要信号强度就需要跟ScanResult数据进行对比后获取.
List<WifiConfiguration> configurationList = mWifiManager.getConfiguredNetworks();
更新指定WiFi(更换密码)
更新WiFi的方法主要是mWifiManager.updateNetwork(item); 但是请注意这个WiFi必须是已经保存配置的WiFi
/** * 更换密码WiFi */ @SuppressLint("MissingPermission") fun changeWifiPwd(wifiData: WifiData, password: String) { val wifiConfigurationList = mWifiManager.configuredNetworks for (item in wifiConfigurationList) { if (item.SSID == null) { continue } if (item.SSID == addQuotationMarks(wifiData.ssid)) { item.preSharedKey = "\"" + password + "\"" mWifiManager.disconnect() val id = mWifiManager.updateNetwork(item) if (id == -1) { //id如果等于 -1 就说明更新失败了 return } mWifiManager.enableNetwork(id, true) //启用连接WiFi mWifiManager.reconnect() } } }
移除指定WiFi
注意! 这里的移除WiFi,只能移除自己的app保存的WiFi,如果是手机-设置-WiFi设置里手动添加的WiFi,你是无法移除的.
/** * 移除保存的WiFi * * @param ssid */ fun removeSaveWifi(wifiData: WifiData) { val wifiConfigurationList = mWifiManager.configuredNetworks for (item in wifiConfigurationList) { if (item.SSID == addQuotationMarks(wifiData.ssid)) { mWifiManager.disconnect() mWifiManager.removeNetwork(item.networkId) mWifiManager.saveConfiguration() mWifiManager.reconnect() } } }
获取当前正在连接中的WiFi信息
public WifiInfo getConnectionInfo() { WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); wifiInfo.getSSID();//WiFi名称 wifiInfo.getRssi();//信号强度 wifiInfo.getIpAddress();//ip地址 wifiInfo.getFrequency();//频率 比如2.4G(boolean is24G = frequency > 2400 && frequency < 2500;) 或者 5G (boolean is5G = frequency > 4900 && frequency < 5900;)的WiFi wifiInfo.getNetworkId();//id wifiInfo.getLinkSpeed();//网络链接速度 wifiInfo.getSupplicantState();//获取请求状态 return wifiInfo; }
这里获取的ssid 是带引号的
注意!获取正在连接中的WiFiInfo,这个数据是永远不会是空的,如果一次WiFi都没连接过那么它携带的数据会有空的,如果连接过然后又断开了,那么你在这里获取的就是之前连接的WiFi. 那么如何判断当前是否真的有连接WiFi呢?
很简单判断ip地址即可,这个地址是实时刷新的:
if (wifiInfo.getIpAddress() == 0){ Log.e(TAG, "getData: 当前未连接WiFi"); }
增加引号和移除引号的方法
/** * 移除引号 * * @param content * @return */ public String removeQuotationMarks(String content) { content = content.substring(1, content.length() - 1); return content; } /** * 添加引号 * * @param content * @return */ public String addQuotationMarks(String content) { content = "\"" + content + "\""; return content; }
格式化wifi信号值
/** * WifiInfo.MIN_RSSI = -126; * WifiInfo.MAX_RSSI = 200; * * Quality Excellent Good Fair Poor * dBm -30 ~ -61 -63 ~ -73 -75 ~ -85 -87 ~ -97 * * @param rssi * @return */ public static int formatLevel(int rssi) { if (rssi < -97){ return 0; }else if (rssi < -87){ return 1; }else if (rssi < -75){ return 2; }else if (rssi < -63){ return 3; }else { return 4; } }
也可以使用WifiManager提供的方法格式化信号强度
int level = WifiManager.calculateSignalLevel(rssi, 5);
将WiFi的IP地址转换成Mac地址显示
/** * 将idAddress转化成string类型的Id字符串 * * @param idString * @return */ private static String getStringId(int idString) { StringBuffer sb = new StringBuffer(); int b = (idString >> 0) & 0xff; sb.append(b + "."); b = (idString >> 8) & 0xff; sb.append(b + "."); b = (idString >> 16) & 0xff; sb.append(b + "."); b = (idString >> 24) & 0xff; sb.append(b); return sb.toString(); }
在WiFi开发中需要使用到的广播
这个很重要,是理解WiFi开发的关键部分
注册广播
private fun registerReceiver(context: Context) { val intentFilter = IntentFilter() //当前连接WiFi状态的变化,这个监听是指当前已经连接WiFi的断开与重连的状态 intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) //WiFi开关状态 intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) //信号变化 intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION) //搜索Wifi扫描已完成,并且结果可用 intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) //表明与接入点建立连接的状态已经改变。一般用来监听当前输入密码后正在连接WiFi的状态(主要状态就是密码错误) intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) context.registerReceiver(mWiFiChangeReceiver, intentFilter) }
/** * WiFi监听 */ inner class WiFiChangeReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { //WiFi开关状态 if (intent.action == WifiManager.WIFI_STATE_CHANGED_ACTION) { /** * switchState状态如下: * WifiManager.WIFI_STATE_DISABLED //WiFi已关闭 * WifiManager.WIFI_STATE_DISABLING //WiFi关闭中 * WifiManager.WIFI_STATE_ENABLED //WiFi已开启 * WifiManager.WIFI_STATE_ENABLING //WiFi开启中 * WifiManager.WIFI_STATE_UNKNOWN //WiFi状态未知 */ val switchState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0)//得到WiFi开关状态值 } //当前连接WiFi状态的变化,这个监听是指当前已经连接WiFi的断开与重连的状态 if (intent.action == WifiManager.NETWORK_STATE_CHANGED_ACTION) { //得到信息包 val parcelableExtra: Parcelable? = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO) /** * networkInfo状态如下: * NetworkInfo.DetailedState.CONNECTED //已经连接 * NetworkInfo.DetailedState.DISCONNECTED //已经断开 * NetworkInfo.DetailedState.IDLE //空闲中 * NetworkInfo.DetailedState.AUTHENTICATING //认证中 * NetworkInfo.DetailedState.BLOCKED //认证失败 * NetworkInfo.DetailedState.CAPTIVE_PORTAL_CHECK //连接检查 */ val networkInfo: NetworkInfo = parcelableExtra as NetworkInfo } //信号变化 if (intent.action == WifiManager.RSSI_CHANGED_ACTION) { //更新当前连接的WiFi信号 val wifiInfo = mWifiManager.connectionInfo val rssi = wifiInfo.rssi } //搜索完成 if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) { val list: List<ScanResult> = mWifiManager.scanResults //获取WiFi列表 } //请求变化 if (intent.action == WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) { val linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0) if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) { //密码错误 Log.e("zh", "onReceive:密码错误 ") } } } }
本人使用建造者模式封装好可以直接使用的WifiHelp类
https://www.cnblogs.com/guanxinjing/p/16307287.html
WIFI热点开发
注意WiFi热点除了WiFi权限以外,如果要创建wifi热点还需要一个系统权限 android.permission.WRITE_SETTINGS。
但是这种系统权限在6.0版本后无法直接静态或者动态授权(十分无语)。所以在下面的代码中setPermissions方法就是添加设置权限的办法。
/* content:创建WiFi热点class time:2018-7-23 11:23 build: */ public class WiFiAP { private static WiFiAP mWiFiAP; private static WifiManager mWifManager; private WiFiAP(){} public static WiFiAP getI(){ if (mWiFiAP == null){ mWiFiAP = new WiFiAP(); } return mWiFiAP; } /** * 手动得到系统权限的方法,提供给外部启动系统权限界面,以实现手动添加系统权限 * @param context 外部activity的上下文 */ public void setPermissions(Context context){ Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse("package:" + context.getPackageName())); context.startActivity(intent); } /** * 打开热点并且创建WiFi热点的方法 * @param context 外部上下文 * @param ssid 要创建WiFi热点的账号名称 * @param password 要创建WiFi热点的密码 * 注意,此方法直接使用WPA2_PSK 的安全策略创建WiFi热点,低版本的Android系统如果需要使用请切换。 */ @SuppressLint("MissingPermission") public void openWiFiAP(Context context, String ssid, String password){ mWifManager = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); if (mWifManager.isWifiEnabled()) { //如果wifi处于打开状态,则关闭wifi, mWifManager.setWifiEnabled(false); } WifiConfiguration config = new WifiConfiguration(); config.SSID = ssid; config.preSharedKey = password; config.hiddenSSID = false;//是否隐藏热点true=隐藏 config.allowedAuthAlgorithms .set(WifiConfiguration.AuthAlgorithm.OPEN);//开放系统认证 config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); int indexOfWPA2_PSK = 4; //从WifiConfiguration.KeyMgmt数组中查找WPA2_PSK的值 for (int i = 0; i < WifiConfiguration.KeyMgmt.strings.length; i++) { if(WifiConfiguration.KeyMgmt.strings[i].equals("WPA2_PSK")) { indexOfWPA2_PSK = i; break; } } //WifiConfiguration.KeyMgmt.WPA_PSK config.allowedKeyManagement.set(indexOfWPA2_PSK); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.TKIP); config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.CCMP); config.status = WifiConfiguration.Status.ENABLED; //通过反射调用设置热点 try { Method method = mWifManager.getClass().getMethod( "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE); boolean enable = (Boolean) method.invoke(mWifManager, config, true); if (enable) { Log.e("WiFiAP", "热点已开启 SSID:" + ssid + " Password:"+password); } else { Log.e("WiFiAP", "创建热点失败"); } } catch (Exception e) { e.printStackTrace(); Log.e("WiFiAP", "创建热点失败"+e); } } /** * 关闭WiFi热点的方法 * @param context 外部activity的上下文 */ public void closeWiFiAP(Context context){ mWifManager = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); if( mWifManager == null){ Log.e("closeWiFiAP", "Error: mWifManager is null"); return; } try { Method method = mWifManager.getClass().getMethod("getWifiApConfiguration"); method.setAccessible(true); WifiConfiguration config = (WifiConfiguration) method.invoke(mWifManager); Method method2 = mWifManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); method2.invoke(mWifManager, config, false); //mText.setText("wifi热点关闭"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
end
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/9708586.html