因公司业务需求 进行WiFi相关的开发,包括WiFi的打开关闭,WiFi监听,自定义弹窗输入密码连接、断开WiFi,保存密码等;
按照以往的方式 ,首先百度 ,cv代码,结果各种报错 ;
先介绍下本地开发机,Android11 的pad,在各大论坛找到的WiFi相关论文等都是针对Android 10 之前的内容 、陈旧,完全不适用;
首先把官网得WiFi说明及操作链接放这里,懒得阅读可以去官网查看;WLAN 扫描功能概览 | Android 开发者 | Android Developers
针对上述问题为甚么不适用呢,由于Android版本迭代较快 ,个人博客不更新,导致滞后,官网相关API过期。因此,需使用最新的API;
下面贴出Wifi操作的最新的API (本人贴出的部分代码是项目中的某些片段,当cv时会提示缺少变量等,不过整体思路。建议参考下官网实例)
private val mWifiManager: WifiManager by lazy { mContext.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager }
//打开、关闭 WiFi开关
mWifiManager.isWifiEnabled =true
大意:api在Android api 29(android 10 )中过期,统一返回false 但在Android 28 (Android 9)之前得版本可以继续使用
打开关闭WiFi API失效
折中得办法是,通过判断是否有网,弹窗跳转设置WiFi界面,手动打开,同理关闭只能手动关闭WiFi;
1. 扫描WiFi
分三步:
1.1.注册广播监听器
1.2.请求扫描
1.3.获取扫描结果
//限制调用函数的次数
private fun timeOut( block: () ->Unit ) {
if ( System.currentTimeMillis() - currentTimeMillis > timeOutMillis){
block.invoke()
}
currentTimeMillis = System.currentTimeMillis()
}
fun build() {
//广播的回调
val broadcastWifi = object : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
try {
timeOut{
intent?.let {
handleReceive(it)
}
}
}catch (e:Exception){
LogTag.d("onReceive::${e}")
}
}
}
//注册广播
val intentFilter = IntentFilter()
mOnWifiSwitch?.let { intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
mContext.registerReceiver(broadcastWifi,intentFilter)
}
mWifiState?.let { intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
intentFilter.addAction(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)
mContext.registerReceiver(broadcastWifi,intentFilter)
startScan()
}
}
//获取结果
private fun handleReceive(intent: Intent) {
when(intent.action){
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION ->{
if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED,false)
){
scanSuccess()
}else{
scanFailure()
}
}
}
}
fun startScan() {
LogTag.d("startScan")
mWifiManager.startScan()
mWifiManager.isWifiEnabled =true
}
private fun scanFailure() {
val results = mWifiManager.scanResults
mWifiState?.stateDisabled(mWifiManager,results)
}
private fun scanSuccess() {
if ( !mWifiManager.isWifiEnabled) {
// mWifiState?.stateDisConnect()
return
}
//mWifiState?.stateEnabled(mWifiManager,mWifiManager.scanResults)
}
这里引申出 “ 节流 ” 这个新名词 ,此处是限制WiFi扫描的次数,即限制wifimanager.startScan();
Android 版本各有差异
下列是Android 8及其以上说明。(不要问我为甚么不贴Android 8之前的)
Android 8.0 和 Android 8.1:
每个后台应用可以在 30 分钟内扫描一次。
Android 9:
每个前台应用可以在 2 分钟内扫描四次。这样便可在短时间内进行多次扫描。
所有后台应用总共可以在 30 分钟内扫描一次。
Android 10 及更高版本:
适用 Android 9 的节流限制。新增一个开发者选项,用户可以关闭节流功能以便进行
本地测试(位于开发者选项 > 网络 > WLAN 扫描调节下)
在实际编码中会遇到SecurityException
需要注意权限,主要是WiFi及位置相关缺少相关导致;
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
当获取到WiFi列表信息后 需按业务整理获取所需信息包括
上图( 拷贝的别人的 ) 大体了解下 相关类库及描述
ps.当注册不同的广播可监听不同的结果
2.连接、断开WiFi 包括 扫描节流、扫描流程
连接WiFi,以下是copy别人的代码 ,未验证。在Android 10 及其以上无效
/**
* ssid: 要连接的ssid
* password: 要连接的密码
* encryptType: 加密方式
*/
public static void connectWifi (Context context, String ssid, String password, int encryptType) {
if (!getWifiEnabled()){
return;
}
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiConfiguration wc = new WifiConfiguration();
wc.allowedAuthAlgorithms.clear();
wc.allowedGroupCiphers.clear();
wc.allowedKeyManagement.clear();
wc.allowedPairwiseCiphers.clear();
wc.allowedProtocols.clear();
wc.SSID = "\"" + ssid + "\"";
WifiConfiguration configuration = getWifiConfig(context, ssid);
if (configuration != null) {
wm.removeNetwork(configuration.networkId);
}
switch (encryptType) {
case 4:
// 不加密
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case 3:
//wep 加密
wc.hiddenSSID = true;
wc.wepKeys[0] = "\"" + password +"\"";
wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case 0:
// wpa/wap2加密
case 1:
// wpa2加密
case 2:
// wpa加密
wc.preSharedKey = "\"" + password + "\"";
wc.hiddenSSID = true;
wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
wc.status = WifiConfiguration.Status.ENABLED;
break;
default:
break;
}
// 如果 network = -1,那么这个wifi是已经连接过的
int network = wm.addNetwork(wc);
if (network == -1){
connectWifi(context,ssid);
}else {
wm.disconnect();
wm.enableNetwork(network, true);
}
}
Android 10 连接WiFi
fun connectWifi(wcp:Params,wn: ConnectivityManager.NetworkCallback) {
if (wcp.mSSid.isNullOrEmpty()){
Throwable("wifi ssid not null")
}
if (wcp.mPs.isNullOrEmpty()){
Throwable("wifi ssid not null")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val specifier = WifiNetworkSpecifier.Builder()
.setSsid(wcp.mSSid!!)
.setWpa2Passphrase(wcp.mPs!!)
.build()
val request =
NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(specifier)
.build()
mConnectivityManager.requestNetwork(request, wn)
} else {
Exception("小于Build.VERSION.SDK_INT,该方法暂未实现")
}
}
2.1.传入ssid 及 密码 通过调用requestNetwork 传入创建的WLAN网络说明符(specifier )实现连接。详情可参考官网:
2.2.连接WiFi后如何判断是否连接成功 ?如果成功系统会在回调对象上调用NetworkCallback.onAvailable()如果失败,则调用NetworkCallback.onUnavailable()。
这里需要普及,在Android10 后通过上述方式建立WiFi 并非在WiFi设置页面操作WiFi ,而是通过自身应用建立的热点,如果退出或删除app,建立的WiFi也将被删除;同样,连接的WiFi只能本app使用 其他应用无法联网使用
3. 自动连接、取消自动连接
其他论坛的代码
public static void connectWifi (Context context, String ssid) {
WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiConfiguration wc = new WifiConfiguration();
wc.SSID = "\"" + ssid + "\"";
WifiConfiguration configuration = getWifiConfig(context, ssid);
if (configuration != null) {
wm.disconnect();
wm.enableNetwork(configuration.networkId, true);
}
}
Android 10 自动连接
//自动连接
fun addNetworkSuggestions(wcp:Params) {
LogTag.d("自动连接Wifi")
if (wcp.mSSid.isNullOrEmpty()){
Throwable("wifi ssid not null")
}
if (wcp.mPs.isNullOrEmpty()){
Throwable("wifi ssid not null")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val suggestion1 = WifiNetworkSuggestion.Builder()
.setSsid(wcp.mSSid!!)
.setWpa2Passphrase(wcp.mPs!!)
.setIsAppInteractionRequired(true)
.build()
val status = mWifiManager.addNetworkSuggestions(listOf(suggestion1))
if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS){
LogTag.d("自动连接失败")
}
} else {
}
}
/**
* 取消自动连接
* 取消全部,可以传入独立的网络说明符取消个别的
*/
fun disConnectWifi() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
mWifiManager.removeNetworkSuggestions(mWifiManager.networkSuggestions)
}else Exception("小于${Build.VERSION.SDK_INT},该方法暂未实现")
}else Exception("小于${Build.VERSION.SDK_INT},该方法暂未实现")
}
4. help类
object NetHelper {
//判断是否联网
fun IsHaveInternet( context: Context): Boolean {
//获取连接管理器
if(isWifiConnect(context) ){
return true
}else if(isShuJuConnect(context) ){
return true
}
return false
}
fun isWifiConnect(context: Context?): Boolean {
if (context != null) {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val info = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
if (info != null) {
return info.isAvailable
}
}
return false
}
/*
* 判断数据流量是否可用
* */
fun isShuJuConnect(context: Context?): Boolean {
if (context != null) {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
if (info != null) {
return info.isAvailable
}
}
return false
}
/**
* 获取加密方式
*/
enum class WifiCapability {
WIFI_CIPHER_WEP, WIFI_CIPHER_WPA, WIFI_CIPHER_NO_PASS
}
fun capabilities(capabilities: String): WifiCapability {
return when {
capabilities.contains("WEB") -> {
WifiCapability.WIFI_CIPHER_WEP
}
capabilities.contains("PSK") -> {
WifiCapability.WIFI_CIPHER_WPA
}
capabilities.contains("WPS") -> {
WifiCapability.WIFI_CIPHER_NO_PASS
}
else -> {
WifiCapability.WIFI_CIPHER_NO_PASS
}
}
}
// 是否是 2.4G
private var is24G = false
// 是否是 5G
private var is5G = false
/**
* 设置频率
*
* @param frequency 频率
*/
fun setFrequency(frequency: Int):String {
if (frequency in 2401..2499) {
is24G = true
return "2.4G"
}
if (frequency in 4901..5899) {
is5G = true
return "5G"
}
return ""
}
}
关于WiFi的相关API很多地方又过期 无法使用,建议直接查官网,官网信息是同步更新的;
如果有什么疑惑或不对的地方,望批评指正,感谢;