Android Kotlin 刷新文本视图

Android Kotlin refresh textview

我想在文本视图中每次更改时更新 android 系统的本地 ip,这是我的代码。

获取ip的函数是这个

fun getIpv4HostAddress(): String {
    NetworkInterface.getNetworkInterfaces()?.toList()?.map { networkInterface ->
        networkInterface.inetAddresses?.toList()?.find {
            !it.isLoopbackAddress && it is Inet4Address
        }?.let { return it.hostAddress }
    }
    return ""
}

MainActivity.tk 的 onCreate 中的代码是这样的

val textView: TextView = findViewById(R.id.getIP)
    textView.setText("IP local: " + getIpv4HostAddress())
    textView.invalidate()

我希望它在 texview 中实时更新和显示,例如在设置和删除飞行模式后,或者更改网络 wifi-> 移动 mobile-> wifi

这里我离开了,如申请中所示,请有人帮助我

要实时接收事件信息,您可以使用不同的方式,具体取决于您的应用在需要信息时是在前台还是后台。

由于在您的情况下应用程序似乎在前台,您利用 application.class 编写代码以使用广播接收器(以编程方式注册)或其他方式接收网络更改。然后在接收该事件更改信息的函数中,调用您的 getIpv4HostAddress() 来设置 ip string 并在另一个 calss 的 textview 集合中使用它。

除了提取 IPv4 地址之外,我几乎已经准备好使用解决方案来解决这个问题,所以我将 post 放在这里,以便您可以使用它。

基本上,该解决方案由两个主要组件组成:侦听网络变化的“服务”和您订阅的 RX subject 以及有关网络变化的 post 更新。

第 0 步:准备

确保您的 AndroidManifest.xml 文件具有以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

您的应用必须启用兼容性选项才能使用 Java 8 项功能。在您的 build.gradle 文件中添加下一行:

android {
    ...
    compileOptions {
        targetCompatibility = "8"
        sourceCompatibility = "8"
    }
}

为了使用 RX Kotlin 添加下一个依赖项:

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.0'

第 1 步:实施网络更改侦听器服务

省略导入以使代码尽可能简洁。 NetworkReachabilityService 不是您可以启动的常规 Android 服务,它会 运行 即使应用程序被终止。它是一个 class 设置监听器 ConnectivityManager 并处理与网络状态相关的所有更新。

任何类型的更新都以类似方式处理:更改了某些内容 -> post NetworkState 具有适当值的对象。在每次更改时,我们都可以请求 IPv4 在 UI 中显示(参见步骤 3)。

sealed class NetworkState {
    data class Available(val type: NetworkType) : NetworkState()
    object Unavailable : NetworkState()
    object Connecting : NetworkState()
    object Losing : NetworkState()
    object Lost : NetworkState()
}

sealed class NetworkType {
    object WiFi : NetworkType()
    object CELL : NetworkType()
    object OTHER : NetworkType()
}

class NetworkReachabilityService private constructor(context: Application) {

    private val connectivityManager: ConnectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        // There are more functions to override!

        override fun onLost(network: Network) {
            super.onLost(network)
            postUpdate(NetworkState.Lost)
        }

        override fun onUnavailable() {
            super.onUnavailable()
            postUpdate(NetworkState.Unavailable)
        }

        override fun onLosing(network: Network, maxMsToLive: Int) {
            super.onLosing(network, maxMsToLive)
            postUpdate(NetworkState.Losing)
        }

        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            updateAvailability(connectivityManager.getNetworkCapabilities(network))
        }

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            super.onCapabilitiesChanged(network, networkCapabilities)
            updateAvailability(networkCapabilities)
        }
    }

    companion object {
        // Subscribe to this subject to get updates on network changes
        val NETWORK_REACHABILITY: BehaviorSubject<NetworkState> =
            BehaviorSubject.createDefault(NetworkState.Unavailable)

        private var INSTANCE: NetworkReachabilityService? = null

        @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
        fun getService(context: Application): NetworkReachabilityService {
            if (INSTANCE == null) {
                INSTANCE = NetworkReachabilityService(context)
            }
            return INSTANCE!!
        }
    }

    private fun updateAvailability(networkCapabilities: NetworkCapabilities?) {
        if (networkCapabilities == null) {
            postUpdate(NetworkState.Unavailable)
            return
        }
        var networkType: NetworkType = NetworkType.OTHER

        if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
            networkType = NetworkType.CELL
        }
        if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) {
            networkType = NetworkType.WiFi
        }

        postUpdate(NetworkState.Available(networkType))
    }

    private fun postUpdate(networkState: NetworkState) {
        NETWORK_REACHABILITY.onNext(networkState)
    }

    fun pauseListeningNetworkChanges() {
        try {
            connectivityManager.unregisterNetworkCallback(networkCallback)
        } catch (e: IllegalArgumentException) {
            // Usually happens only once if: "NetworkCallback was not registered"
        }
    }

    fun resumeListeningNetworkChanges() {
        pauseListeningNetworkChanges()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            connectivityManager.registerDefaultNetworkCallback(networkCallback)
        } else {
            connectivityManager.registerNetworkCallback(
                NetworkRequest.Builder().build(),
                networkCallback
            )
        }
    }
}

第 2 步:实现提取 IPv4 的方法(奖励 IPv6)

我不得不稍微修改您的 IPv4 提取,因为它没有 return 任何 IPv4 地址,而设备显然有一个。这是分别提取 IPv4 和 IPv6 地址的两种方法。使用 this SO answer 修改了如何提取 IP 地址的方法。总体而言,inetAddresses 到 IP 地址值的映射有 90% 相同。

将这两个方法添加到NetworkReachabilityService class:

fun getIpv4HostAddress(): String? =
    NetworkInterface.getNetworkInterfaces()?.toList()?.mapNotNull { networkInterface ->
        networkInterface.inetAddresses?.toList()
            ?.filter { !it.isLoopbackAddress && it.hostAddress.indexOf(':') < 0 }
            ?.mapNotNull { if (it.hostAddress.isNullOrBlank()) null else it.hostAddress }
            ?.firstOrNull { it.isNotEmpty() }
    }?.firstOrNull()

fun getIpv6HostAddress(): String? =
    NetworkInterface.getNetworkInterfaces()?.toList()?.mapNotNull { networkInterface ->
        networkInterface.inetAddresses?.toList()
            ?.filter { !it.isLoopbackAddress && it is Inet6Address }
            ?.mapNotNull { if (it.hostAddress.isNullOrBlank()) null else it.hostAddress }
            ?.firstOrNull { it.isNotEmpty() }
    }?.firstOrNull()

第 3 步:更新 UI

与 UI 相关的简单解决方案是直接订阅 NETWORK_REACHABILITY 主题,并且在通过该主题收到的每个更改中,我们从 NetworkReachabilityService 中提取 IPv4 数据并将其显示在UI。您要查看的两个主要方法是 subscribeToUpdatesupdateIPv4Address。并且不要忘记使用 unsubscribeFromUpdates 取消订阅以防止内存泄漏。

class MainActivity : AppCompatActivity() {

    private val compositeDisposable = CompositeDisposable()
    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView = findViewById(R.id.text_view)

        val service = NetworkReachabilityService.getService(application)
        service.resumeListeningNetworkChanges()

        subscribeToUpdates()
    }

    override fun onDestroy() {
        super.onDestroy()
        unsubscribeFromUpdates()
    }

    private fun unsubscribeFromUpdates() {
        compositeDisposable.dispose()
        compositeDisposable.clear()
    }

    private fun subscribeToUpdates() {
        val disposableSubscription =
            NetworkReachabilityService.NETWORK_REACHABILITY
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ networkState ->
                    // We do not care about networkState right now
                    updateIPv4Address()
                }, {
                    // Handle the error
                    it.printStackTrace()
                })

        compositeDisposable.addAll(disposableSubscription)
    }

    private fun updateIPv4Address() {
        val service = NetworkReachabilityService.getService(application)
        textView.text = service.getIpv4HostAddress()
    }
}

回顾

使用 ConnectivityManager 实例,我们设置了一个监听器,它对任何网络变化做出反应。每次更改都会触发更新,其中 posts 对持有最新网络状态的 RX 主体的价值。通过订阅主题,我们可以跟踪网络状态变化并假设设备的地址已更改,因此我们刷新 TextView.

中显示的 IPv4 值

我认为这段代码可以继续 GitHub,所以这里是 the link to the project