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操作"http://網絡名稱
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))
就一句話搞定