2023.9.25更新:
这个文章只能提供作为参考,前段时间调研了一下android应用内连接wifi的情况,
目前有三种方式
1.直接添加wifi配置,直接进行连接(android 10及以后禁止添加wifi配置 想通过此方法在高版本android实现连接 只能将项目target sdk设置为小于等于28 但这样做又会导致谷歌市场拒绝上架 国内应用市场似乎不受影响)
2.通过suggest进行连接(系统看网络情况决定是否要连接)
3.通过平p2p方式连接 (无法上网 用于数据传输场景 不能用)
代码只有参考作用 推荐方式1 但要将target sdk设置为小于等于28 如果你想上架谷歌市场,目前没有一个比较好的方案实现应用内连接
本文主要参考了官方的开发文档
适用于互联网连接的 WLAN 建议 API
适用于对等连接的 WLAN 网络请求 API
一.Android 10版本和10以下关于wifi连接的区别
1.Android10不允许应用添加系统的网络配置,但是官方提供了一个新的方案来让应用进行连接wifi
这个新的方案就是“向系统提建议”,就是我在应用中告诉系统,这里有一个wifi可以连接,它的名称是什么什么,密码是什么什么。
系统收到这个建议后,会根据不同情况来决定是否要接受这个应用的建议。如果接受了就会发出广播通知你,提出建议后只需要准备好一个广播接收器就好了
但是实际测试下来感觉系统很高傲呀,在本身已经连接其他wifi的情况下根本就不会理你,这该怎么办呢
我在文档上还发现了一个P2P的连接:适用于对等连接的 WLAN 网络请求 API
尝试用这个来进行wifi连接,居然可行,那就暂时先这样了
2.Android10不允许应用打开/关闭wifi开关
这个没办法,Android 10只能打开设置中的wifi界面让用户自己打开了
二. 一个wifi连接工具类
使用之前一定要请求位置权限,因为要获取wifi列表,而根据wifi列表是可以计算出位置信息的
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
wifi连接工具类:
@SuppressLint("MissingPermission")
class WifiTools {
//位置权限!!
companion object {
val instance: WifiTools by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { WifiTools() }
}
private val TAG = "wifi操作"//网络名称
private val context = App.context
private val wifiManager =
context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
@SuppressLint("MissingPermission")
fun connectWifi(ssid: String, password: String) {
openWifi()
val resssss=wifiManager.scanResults
resssss.size
val scanResult = wifiManager.scanResults.singleOrNull { it.SSID == ssid }
if (scanResult == null) {
Toast.makeText(context, context.getString(R.string.search_wifi_fail), Toast.LENGTH_SHORT).show()
return
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
connectByP2P(ssid, password)
return
}
var isSuccess = false
//如果找到了wifi了,从配置表中搜索该wifi的配置config,也就是以前有没有连接过
//注意configuredNetworks中的ssid,系统源码中加上了双引号,这里比对的时候要去掉
val config =
wifiManager.configuredNetworks.singleOrNull { it.SSID.replace("\"", "") == ssid }
isSuccess = if (config != null) {
//如果找到了,那么直接连接,不要调用wifiManager.addNetwork 这个方法会更改config的!
wifiManager.enableNetwork(config.networkId, true)
} else {
// 没找到的话,就创建一个新的配置,然后正常的addNetWork、enableNetwork即可
val padWifiNetwork =
createWifiConfig(
scanResult.SSID,
password,
getCipherType(scanResult.capabilities)
)
val netId = wifiManager.addNetwork(padWifiNetwork)
wifiManager.enableNetwork(netId, true)
}
if (isSuccess) {
Toast.makeText(context, context.getString(R.string.connect_success), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, context.getString(R.string.connect_fail), Toast.LENGTH_SHORT).show()
}
}
}
private fun openWifi() {
if (!wifiManager.isWifiEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//请用户手动打开wifi
Toast.makeText(context, context.getString(R.string.open_wifi_hint), Toast.LENGTH_SHORT).show()
//这里可以使用event bus代替 在activity接收到后 打开wifi的设置界面
DarkmagicMessageManager.send(MessageAction.OPENWIFISETTING)
} else {
wifiManager.isWifiEnabled = true
}
}
}
private fun startScantWifi() {
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "Wifi扫描完成")
val results = wifiManager.scanResults//结果
}
}
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)
wifiManager.startScan()
}
//Android8以下 通过Config连接Wifi
private fun connectByConfig() {
}
//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);
}
private fun createWifiConfig(
ssid: String,
password: String,
type: WifiCapability
): WifiConfiguration {
//初始化WifiConfiguration
val config = WifiConfiguration()
config.allowedAuthAlgorithms.clear()
config.allowedGroupCiphers.clear()
config.allowedKeyManagement.clear()
config.allowedPairwiseCiphers.clear()
config.allowedProtocols.clear()
//指定对应的SSID
config.SSID = "\"" + ssid + "\""
//如果之前有类似的配置
val tempConfig = wifiManager.configuredNetworks.singleOrNull { it.SSID == "\"$ssid\"" }
if (tempConfig != null) {
//则清除旧有配置 不是自己创建的network 这里其实是删不掉的
wifiManager.removeNetwork(tempConfig.networkId)
wifiManager.saveConfiguration()
}
//不需要密码的场景
if (type == WifiCapability.WIFI_CIPHER_NO_PASS) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
//以WEP加密的场景
} else if (type == WifiCapability.WIFI_CIPHER_WEP) {
config.hiddenSSID = true
config.wepKeys[0] = "\"" + password + "\""
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED)
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
config.wepTxKeyIndex = 0
//以WPA加密的场景,自己测试时,发现热点以WPA2建立时,同样可以用这种配置连接
} else if (type == WifiCapability.WIFI_CIPHER_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
}
private fun getCipherType(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
}
}
}
}
enum class WifiCapability {
WIFI_CIPHER_WEP, WIFI_CIPHER_WPA, WIFI_CIPHER_NO_PASS
}
注释也给的很清晰,简单说一下,android10采用p2p连接wifi,android10以下采用原本的添加wifi配置的方式进行连接。
用法:
WifiTools.instance.connectWifi(ssid, password)
三. 后续一些想说的话
以后遇到问题多多研究一下官方的开发者文档,真的非常详细且规范。很多问题国内的各家CSDN,博客园,甚至简书,内容都太陈旧过时了。只有官方我文档是和系统同步进行更新的。
在实际测试中,发现竞品居然可以在android10上直接连接wifi,这是我没有想到的。也就是说这个P2P连接wifi的方式并非最优解,看来学习之路漫漫呀
对了关于打开系统设置的wifi界面代码在这里:
startActivity(Intent(android.provider.Settings.ACTION_WIFI_SETTINGS))
就一句话搞定