一、前言
最近在做一个wifi相关的项目,因此去了解了官方文档和相关技术资料,整理了一下目前wifi连接的可行性和办法,包括扫描、连接、状态监听。
二、使用方法
目前安卓10开始官方已经弃用原来的连接wifi了,改用对等连接:developer.android.com/guide/topic… 而这个对等连接其实就是APP内部连接到该wifi,但是手机本身是保持原来连接的wifi的,所以原来那种修改系统wifi连接的已经弃用了,想继续使用,需要把项目targetSdkVersion修改为小于29。
1.wifi管理类
object WifiUtils {
val mWifiManager by lazy {
WiFiMoneyApplication.getApplication ()?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
}
开启wifi
fun openWifi() {
mWifiManager.isWifiEnabled = true
}
连接wifi
@SuppressLint("MissingPermission")
fun connectWifi(context: Context, scanResults: List<ScanResult>, ssid: String, connect: (success: Boolean, scanResult: ScanResult) -> Unit) {
if (!mWifiManager.isWifiEnabled) {
return
}
val scanResult = scanResults.singleOrNull { it.SSID == ssid }
if (scanResult != null) {
val config = mWifiManager.configuredNetworks.singleOrNull { it.SSID.replace("\"", "") == ssid }
if (config != null && config.status != WifiConfiguration.Status.DISABLED) {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "找到了历史wifi:${scanResult.SSID}")
}
connect.invoke(mWifiManager.enableNetwork(config.networkId, true), scanResult)
} else {
val type = getCipherType(scanResult.capabilities)
if (type == WifiCapability.WIFI_CIPHER_NO_PASS) {
// 没找到历史wifi
val padWifiNetwork =
createWifiConfig(
scanResult.SSID,
type = getCipherType(scanResult.capabilities))
val netId = mWifiManager.addNetwork(padWifiNetwork)
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "不需要密码连接wifi:${scanResult.SSID}")
}
connect.invoke(mWifiManager.enableNetwork(netId, true), scanResult)
} else {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "需要密码连接wifi:${scanResult.SSID}")
}
connect.invoke(false, scanResult)
}
}
} else {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "connectWifi 没有找到")
}
}
}
@SuppressLint("MissingPermission")
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 = mWifiManager.configuredNetworks.singleOrNull { it.BSSID == "\"$ssid\"" }
if (tempConfig != null) {
//则清除旧有配置 不是自己创建的network 这里其实是删不掉的
val isDisable = mWifiManager.disableNetwork(tempConfig.networkId)
val isRemove = mWifiManager.removeNetwork(tempConfig.networkId)
val isSave = mWifiManager.saveConfiguration()
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "清除wifi配置:${tempConfig.SSID + (isDisable && isRemove && isSave)}")
}
}
//不需要密码的场景
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加密的场景
} 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
}
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
}
外部调用
fun connectWifi(item: ScanResult) {
if (item.BSSID == WifiUtils.mWifiManager.connectionInfo.bssid) {
ToastUtils.showToast(context, getString(R.string.wifi_home_connect_error))
return
}
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "开始切换wifi")
}
WifiUtils.connectWifi(context!!, mWifiList, item.SSID) { success, scanResult ->
if (success) {
// 这个时候找到wifi,开始连接
} else {
// 未成功找到wifi添加,尝试输入密码
activity?.let { activity ->
InputPasswordDialog(activity, scanResult.SSID) { password ->
val padWifiNetwork =
WifiUtils.createWifiConfig(
scanResult.SSID,
password,
WifiUtils.getCipherType(scanResult.capabilities)
)
val netId = WifiUtils.mWifiManager.addNetwork(padWifiNetwork)
val success2 = WifiUtils.mWifiManager.enableNetwork(netId, true)
if (success2) {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "这个时候找到wifi,开始连接:${success2}")
}
startConnectAni(item)
} else {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "创建连接失败")
}
ToastUtils.showToast(context!!, "创建连接失败")
}
}
}?.show()
}
}
}
wifi强度获取
fun getNetworkWifiLevel(wifiInfo: ScanResult): Int {
//获得信号强度值
val level = wifiInfo.level
//根据获得信号的强度发送信息
return if (level <= 0 && level >= -50) { //最强
5
}
else if (level < -50 && level >= -70) { //较强
4
}
else if (level < -70 && level >= -80) { //较弱
3
}
else if (level < -80 && level >= -100) { //微弱
2
}
else {
1
}
}
2.wifi连接状态监听
private fun registerWifiStatusListener() {
val filter = IntentFilter()
filter.addAction(SUPPLICANT_STATE_CHANGED_ACTION)// wifi连接结果
filter.addAction(WIFI_STATE_CHANGED_ACTION) // wifi开启关闭状态
filter.addAction(NETWORK_STATE_CHANGED_ACTION)// wifi连接状态
filter.addAction(SCAN_RESULTS_AVAILABLE_ACTION)// 监听wifi列表变化(开启一个热点或者关闭一个热点)
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE")// 网络类型切换
activity?.application?.registerReceiver(mWifiReceiver, filter)
}
private val mWifiReceiver: BroadcastReceiver by lazy {
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action === WIFI_STATE_CHANGED_ACTION) {
when (intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_UNKNOWN)) {
// 已关闭
WIFI_STATE_DISABLED -> {
updateWifiStatus(6)
}
// 关闭中
WIFI_STATE_DISABLING -> {}
// 可用
WIFI_STATE_ENABLED -> {}
// 正在开启
WIFI_STATE_ENABLING -> {
updateWifiStatus(5)
}
// 未知
WIFI_STATE_UNKNOWN -> {
updateWifiStatus(1)
}
}
} else if (NETWORK_STATE_CHANGED_ACTION == intent.getAction()) {
val info: NetworkInfo = intent.getParcelableExtra(EXTRA_NETWORK_INFO)
if (NetworkInfo.State.DISCONNECTED == info.state) { // wifi没连接上,连接已断开
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "连接已断开")
}
} else if (NetworkInfo.State.CONNECTED == info.state) { //wifi连接上了
if (BuildConfig.LOG_DEBUG) {
Log.d("TAG, "wifi连接上了")
}
refreshWifiList()
} else if (NetworkInfo.State.CONNECTING == info.state) { // 正在连接
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "wifi正在连接")
}
}
} else if (SCAN_RESULTS_AVAILABLE_ACTION == intent.action) {
val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
if (success) {
scanSuccess()
} else {
scanFailure()
}
// wifi连接结果通知 WIFI连接请求状态发生改变
} else if (SUPPLICANT_STATE_CHANGED_ACTION == intent.action) {
// 密码错误
val error = intent.getIntExtra(EXTRA_SUPPLICANT_ERROR, -1)
if (error == ERROR_AUTHENTICATING) {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:密码错误")
}
// todo 连接失败
return
}
// 获取连接状态
val supplicantState: SupplicantState =
intent.getParcelableExtra(EXTRA_NEW_STATE)
when (supplicantState) {
// 成功
SupplicantState.COMPLETED -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:成功")
}
// todo 连接成功
}
// 不活跃的
SupplicantState.INACTIVE -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:不活跃的")
}
// todo 连接失败
}
// 接口禁用
SupplicantState.INTERFACE_DISABLED -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:接口禁用")
}
}
SupplicantState.DISCONNECTED -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:断开连接")
}
}
SupplicantState.SCANNING -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:正在扫描")
}
}
SupplicantState.AUTHENTICATING -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:正在验证")
}
}
SupplicantState.ASSOCIATING -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:正在关联")
}
}
SupplicantState.ASSOCIATED -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:已经关联")
}
}
SupplicantState.FOUR_WAY_HANDSHAKE -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:四次握手")
}
}
SupplicantState.GROUP_HANDSHAKE -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:组握手")
}
}
SupplicantState.DORMANT -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:休眠")
}
}
SupplicantState.UNINITIALIZED -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:未初始化")
}
}
SupplicantState.INVALID -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "获取连接状态:无效的")
}
}
else -> {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "wifi连接结果通知")
}
}
}
} else if ("android.net.conn.CONNECTIVITY_CHANGE" == intent.action) {
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "网络类型切换了")
}
updateNetWorkType()
}
}
}
}
3.wifi相关权限申请
RxPermissions(this).request(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_WIFI_STATE
).subscribe {
if (!it) {
//没权限
}
}
4.wifi列表更新
fun refreshWifiList() {
if (!WifiUtils.mWifiManager.isWifiEnabled) {
return
}
mWifiList.clear()
// 原始wifi列表
val originList = WifiUtils.mWifiManager.scanResults
// 当前wifi
val currentWifi = originList.singleOrNull {
it.BSSID == WifiUtils.mWifiManager.connectionInfo.bssid &&
WifiUtils.mWifiManager.connectionInfo.supplicantState == SupplicantState.COMPLETED
}
// 添加wifi到列表
for ((i, _) in originList.withIndex()) {
// 该热点SSID是否已在列表中
val position: Int = getItemPosition(mWifiList, originList[i])
// 过滤没有名字的,没有意义
if (originList[i].SSID.isNotEmpty()) {
if (position != -1) { // 已在列表
// 相同SSID热点,取信号强的
if (mWifiList[position].level < originList[i].level) {
mWifiList.removeAt(position)
mWifiList.add(position, originList[i])
}
} else {
mWifiList.add(originList[i])
}
}
// 按信号强度排序
mWifiList.sortWith(Comparator { t1, t2 -> t2.level - t1.level })
}
// 置顶当前连接的wifi
var p = -1
currentWifi?.let {
for ((i, value) in mWifiList.withIndex()) {
if (currentWifi.BSSID == value.BSSID) {
p = i
}
}
updateWifiName()
}
if (p != -1 && currentWifi != null) {
mWifiList.removeAt(p)
mWifiList.add(0, currentWifi)
mWifiAdapter.connectPosition = 0
} else {
mWifiAdapter.connectPosition = -1
}
mWifiAdapter.notifyDataSetChanged()
// 处于连接中的wifi,成功了才切换状态
if (mLastStatus != 2 || (mLastStatus == 2 && currentWifi != null)) {
if (mWifiList.isNotEmpty()) {
updateWifiStatus(1)
} else {
updateWifiStatus(4)
}
}
// 刷新列表
updateListOpenOrNot()
}
5.正在使用的网络类型判断(在监听关闭wifi网络的时候,调用这个依然返回有网络)
object NetworkUtils {
const val NETWORK_NOT_DEFINE = -2 // 没有定义
const val NETWORK_NONE = -1 // 没有网络连接
const val NETWORK_WIFI = 1 // wifi连接
const val NETWORK_2G = 2 // 2G
const val NETWORK_3G = 3 // 3G
const val NETWORK_4G = 4 // 4G
const val NETWORK_5G = 5 // 5G
const val NETWORK_MOBILE = 0 // 手机流量
/**
* 获取当前网络连接的类型
*
* @param context context
* @return int
*/
fun getNetworkState(context: Context): Int {
val connManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// 获取网络类型,如果为空,返回无网络
val activeNetInfo = connManager.activeNetworkInfo
if (activeNetInfo == null || !activeNetInfo.isAvailable) {
return NETWORK_NONE
}
// 判断是否为WIFI
val wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
if (null != wifiInfo) {
val state = wifiInfo.state
if (null != state) {
if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
return NETWORK_WIFI
}
}
}
// 若不是WIFI,则去判断是2G、3G、4G网
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val networkType = telephonyManager.networkType
return when (networkType) {
TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_IDEN -> NETWORK_2G
TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyManager.NETWORK_TYPE_HSPAP -> NETWORK_3G
TelephonyManager.NETWORK_TYPE_LTE -> NETWORK_4G
TelephonyManager.NETWORK_TYPE_NR -> NETWORK_5G
else -> NETWORK_MOBILE
}
}
/**
* 是否正在用手机流量
*/
fun isMobileNetwork (context: Context): Boolean {
val a = getNetworkState(context)
if (BuildConfig.LOG_DEBUG) {
Log.d(TAG, "是否正在用手机流量 getNetworkState:${a}")
}
return a != NETWORK_NONE && a != NETWORK_NOT_DEFINE && a != NETWORK_WIFI
}
}
6.wifi相关api需要定位功能,需要判断用户有没有开定位
/**
* 手机是否开启位置服务
*/
fun isLocServiceEnable(context: Context): Boolean {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
val network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
return gps || network
}
7.android10以上新功能,也列出来吧。
P2P连接Wifi就是对等连接,在APP内部会出现对话框,请求某个wifi连接,仅APP内部使用
// Android10以上 通过P2P连接Wifi
@RequiresApi(Build.VERSION_CODES.Q)
fun connectByP2P(context: Context, scanResult: ScanResult, password: String, successListener: ((Network?) -> Unit)? = null, failListener: (() -> Unit)? = null) {
val specifier = WifiNetworkSpecifier.Builder()
.setSsidPattern(PatternMatcher(scanResult.SSID, PatternMatcher.PATTERN_PREFIX))
.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?) {
connectivityManager.bindProcessToNetwork(network)
successListener?.invoke(network)
}
override fun onUnavailable() {
failListener?.invoke()
}
}
connectivityManager.requestNetwork(request, networkCallback)
// 使用完毕后调用这个方法 connectivityManager.unregisterNetworkCallback(networkCallback)
}
这个是给系统提建议,个人感觉非常鸡肋
// Android10以上,通过suggestion连接WIFI
@RequiresApi(Build.VERSION_CODES.Q)
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)
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的监听真的非常多,估计并没有列出完全监听,欢迎大家可以补充,有问题可以评论讨论。