WifiManager的getConnectionInfo被弃用了?快来使用ConnectivityManager获取更全的网络信息吧

前言

最近在使用flutter写桌面端的一个adb工具,可以使用adb命令无线连接设备,需要电脑和手机在同一局域网内,但是需要手机的ip地址。于是我想到写一个android桌面小组件,点一下就获取WiFi的ipv4地址并显示出来,先去找gpt问了一下,告诉我使用WifiManager ,浅看一下逻辑非常简单 但是....用起来才发现被弃用了,本着遵循官方的建议,还是去寻找一下替代的方法吧

实现思路

去官网翻文档发现WifiInfo被移动到ConnectivityManager中的NetworkCapabilities#getTransportInfo(),官方还提示你可以继续用这个被弃用的api,但是这个api不会被支持新的功能了 而获取IP地址的方法被移动到了android.net.LinkProperties这个类中 官方还贴了一个例子,大体思路就是先获取ConnectivityManager的实例,然后创建一个networkRequest请求,注册networkcallback的监听,就可以在wifi变化时监听到网络信息了

使用callback监听网络信息

MainActivity.kt

kotlin 复制代码
import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {

    private val TAG = "wifiState"

    var handler = object : Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                0 -> {
                    val linkProperties = msg.obj as LinkProperties
                    findViewById<TextView>(R.id.ip).text = "${linkProperties.linkAddresses[1]}"
                }
                1->{
                    findViewById<TextView>(R.id.ip).text = "无连接"
                }
            }
        }
    }

    private val networkCallback = object : NetworkCallback(){
        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            Log.w(TAG, "onAvailable: " )
        }

        override fun onLinkPropertiesChanged(
            network: Network,
            linkProperties: LinkProperties
        ) {
            super.onLinkPropertiesChanged(network, linkProperties)
            Log.w(TAG, "onLinkPropertiesChanged: ${linkProperties.linkAddresses}" )
            handler.sendMessage(Message.obtain(handler,0,linkProperties))
        }

        override fun onUnavailable() {
            super.onUnavailable()
            Log.w(TAG, "onUnavailable: " )
        }

        override fun onLost(network: Network) {
            super.onLost(network)
            handler.sendMessage(Message.obtain(handler,1))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        registerWifiState()

    }

    override fun onStop() {
        super.onStop()
        unregisterWifiState()
    }


    private fun registerWifiState(){
        val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val request = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build()
        connectivityManager.registerNetworkCallback(request, networkCallback)
    }

    private fun unregisterWifiState(){
        val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        connectivityManager.unregisterNetworkCallback(networkCallback)
    }

}

在onStart中注册了一个networkcallback回调,就可以在networkcallback中的onLinkPropertiesChanged中拿到IP地址并且传给id为ip的TextView,如果断开wifi的时候就会设置为无连接。

实现显示IP地址的小组件

目前为止我们已经成功在App内获取到了ip地址,是使用callback的办法,那能不能通过点击获取实时的网络ip呢,当然可以,但我们要使用小组件的方式来实现,就需要先写一个简单的小组件出来 先写一个小组件广播 WifiIpWidget.kt

kotlin 复制代码
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.wifi.WifiManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.RemoteViews
import demo.tdsss.wifistate.R

/**
 * @author TDSSS
 * @datetime 2023/11/22 17:47
 */
class WifiIpWidget : AppWidgetProvider() {

    private val TAG = "wifi state widget"

    override fun onEnabled(context: Context?) {
        super.onEnabled(context)
        updateInfo(context)
        Log.w(TAG, "onEnabled: " )
    }

    override fun onUpdate(
        context: Context?,
        appWidgetManager: AppWidgetManager?,
        appWidgetIds: IntArray?
            ) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
        Log.w(TAG, "onUpdate: " )
        updateInfo(context)
    }

    override fun onAppWidgetOptionsChanged(
        context: Context?,
        appWidgetManager: AppWidgetManager?,
        appWidgetId: Int,
        newOptions: Bundle?
            ) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
        Log.w(TAG, "onAppWidgetOptionsChanged: " )
        val intent = Intent(context,this.javaClass)
        intent.action = "touch"
        val remoteViews = RemoteViews(context?.packageName, R.layout.widget_layout).also {
            it.setOnClickPendingIntent(
                R.id.wifi_widget,
                PendingIntent.getBroadcast(
                    context,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                ))
        }
        appWidgetManager!!.partiallyUpdateAppWidget(appWidgetId, remoteViews)
    }

    override fun onDisabled(context: Context?) {
        super.onDisabled(context)
        Log.w(TAG, "onDisabled: " )
    }

    override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
        super.onDeleted(context, appWidgetIds)
        Log.w(TAG, "onDeleted: ", )
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        super.onReceive(context, intent)
        //        Log.w(TAG, "onReceive: " )
        //        Log.w(TAG, "action: ${intent?.action}" )
        //        updateInfo(context)
        if(intent?.action == "touch"){
            Log.w(TAG, "onReceive: action == touch" )
            updateInfo(context)
        }
        if (intent?.action != null && intent.action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            Log.w(TAG, "onReceive: NETWORK_STATE_CHANGED_ACTION" )
            updateInfo(context)
        }
    }

    private fun updateInfo(context : Context?){
        val connectivityManager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkInfo = connectivityManager.activeNetwork
        val capabilities = connectivityManager.getNetworkCapabilities(networkInfo)
        if(capabilities == null || !(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))){
            val appWidgetManager = AppWidgetManager.getInstance(context)
            val ids = appWidgetManager.getAppWidgetIds(ComponentName(context,this.javaClass))
            val remoteViews = RemoteViews(context.packageName, R.layout.widget_layout).also {
                it.setTextViewText(R.id.wifiAddress, "未连接wifi")
            }
            appWidgetManager.partiallyUpdateAppWidget(ids, remoteViews)
            return
        }
        val linkProperties = connectivityManager.getLinkProperties(networkInfo)
        val address = linkProperties?.linkAddresses
        Log.w(TAG, "onReceive address: $address" )
        val appWidgetManager = AppWidgetManager.getInstance(context)
        val ids = appWidgetManager.getAppWidgetIds(ComponentName(context,this.javaClass))
        val intent = Intent(context,this.javaClass)
        intent.action = "touch"
        val remoteViews = RemoteViews(context.packageName, R.layout.widget_layout).also {
            it.setTextViewText(R.id.wifiAddress, "${address?.get(1)}")
            it.setOnClickPendingIntent(
                R.id.wifi_widget,
                PendingIntent.getBroadcast(
                    context,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                ))
        }
        appWidgetManager.partiallyUpdateAppWidget(ids, remoteViews)
    }
}

核心逻辑其实就在updateInfo()中,先通过connectivityManager获取当前的networkInfo,然后根据networkInfo获取NetworkCapabilities,判断网络连接类型是否为WiFi,如果不仅仅想获取WiFi的就不用判断类型 然后通过connectivityManager和networkInfo获取LinkProperties,再从LinkProperties的实例中获取linkAddresses就可以啦,这里的linkAddresses其实是一个List类型,里面可能会有多端IP地址,包含IPv6和IPv4的,看你需要什么就取什么。

小组件布局很简单,就两行文字 widget_layout.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:background="@color/white"
  android:id="@+id/wifi_widget"
  >
  <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="点击获取Wi-Fi地址:" />

  <TextView
    android:id="@+id/wifiAddress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="未连接" />

</LinearLayout>

别忘了在AndroidManifest.xml中注册小组件广播以及权限添加

xml 复制代码
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<receiver android:name=".widget.WifiIpWidget"
  android:exported="false"
  >
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    <action android:name="android.net.wifi.STATE_CHANGE"/>
  </intent-filter>
  <meta-data android:name="android.appwidget.provider"
    android:resource="@xml/wifi_ip_widget_info" />
</receiver>

注:这里我们虽然静态注册了"android.net.wifi.STATE_CHANGE"的wifi变化广播监听,但其实在Android O(8.0)即API 26之后,静态广播就受到限制,如果想让小组件在onReceiver中实时监听网络信息需要修改target Sdk 为26以下(不包含26),详细信息可以查看文档↓ 广播概览 | Android 开发者 | Android Developers

最后我们安装完App,把小组件添加到桌面上,点击就会实时获取当前WiFi的局域网IP地址啦 源码地址:github.com/TDSSSzero/A...

相关推荐
爱装代码的小瓶子1 小时前
【C++与Linux基础】进程间通讯方式:匿名管道
android·c++·后端
兴趣使然HX1 小时前
Android绘帧流程解析
android
JMchen1232 小时前
Android UDP编程:实现高效实时通信的全面指南
android·经验分享·网络协议·udp·kotlin
黄林晴3 小时前
Android 17 再曝猛料:通知栏和快捷设置终于分家了,这操作等了十年
android
有位神秘人3 小时前
Android获取设备中本地音频
android·音视频
JMchen1233 小时前
Android网络安全实战:从HTTPS到双向认证
android·经验分享·网络协议·安全·web安全·https·kotlin
CS创新实验室3 小时前
Pandas 3 的新功能
android·ide·pandas
ujainu4 小时前
护眼又美观:Flutter + OpenHarmony 鸿蒙记事本一键切换夜间模式(四)
android·flutter·harmonyos
三少爷的鞋4 小时前
为什么我不在 Android ViewModel 中直接处理异常?
android
草莓熊Lotso5 小时前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能