如何在Android中使用LiveData处理互联网连接和能力

306 阅读12分钟

如何使用Android中的LiveData处理互联网连接和能力

在开发一个Android应用程序时,你可能要从远程服务器上消耗数据。这将需要用户有一个互联网连接。

以一个用户登录系统为例进行比较。你需要对该用户进行认证。因此,这个过程必须在互联网上来回撞击,以确保提交的凭证对该特定用户有效。用户需要有Wi-Fi连接或移动数据来处理所有这些服务器请求和响应。

用户可能不知道他们的手机没有互联网连接。作为一个开发者,你需要为这种情况准备你的应用程序。应用程序应该提醒用户,他们需要互联网才能访问应用程序的信息。

然而,作为一个开发者,处理这种情况的方法必须证明其对用户的重要性。例如,你需要检测和监控网络连接。然后决定告诉用户什么。

在这种情况下,开发一个只是检测网络连接的应用程序可能会欺骗用户。当连接到互联网时,你的应用程序仍将工作并显示。

然而,你不能只依赖网络连接的检测。用户可能连接到了Wi-Fi,但Wi-Fi没有活动的互联网来连接和访问服务器的数据。这意味着你必须首先检测用户是否已经连接到互联网,然后监测用户的网络以了解互联网的能力。

如果这个连接是活跃的,它是否有能力访问在线服务器/数据?活跃的互联网连接并不是网络连接能力的保证。了解这些能力将有助于你向用户返回正确的信息。

你需要检测网络,检测你何时连接到一个网络,然后测试该网络是否有互联网连接。这意味着你需要实时监控和检查互联网连接,并向用户显示连接状态。

本文旨在解释这个概念,并在Android应用程序中使用Kotlin实现它。

前提条件

要跟上本指南,你需要。

  • 确保你正在使用最新版本的Android Studio。
  • 具备一些关于如何使用Android studio IDE和Kotlin的基本知识。

背景介绍

我们正在使用ConnectivityManager() 来监听网络连接的变化。

然后我们将把这些变化保存到LiveData 对象。LiveData对象将作为一个数据持有者,可以被UI组件观察到。这使得屏幕上显示的内容更容易与保存在LiveData对象中的数据保持同步。让我们简单地讨论一下这些概念。

实时数据

检查和监控网络连接应该是直截了当的。这意味着这个操作应该根据用户与网络连接和应用程序本身的互动情况来实时检查。

你需要跟踪Android应用程序的不同状态和生命周期,以检查你的应用程序组件中的不同更新,以向用户显示它何时连接和何时失去连接。

LiveData是Android架构组件的一部分。它具有生命周期意识,意味着它尊重其他应用组件的生命周期,如活动片段或服务。这种意识确保LiveData只更新处于活动生命周期状态的应用组件观察者。

简单地说,UI观察这个LiveData对象并得到更新的通知。因此,当LiveData发生变化时,UI会得到通知,然后UI可以用新的数据重新绘制自己。LiveData使得保持屏幕上的内容与数据同步变得容易。

现在你可能想知道LiveData的概念与互联网连接的关系在哪里。在这种情况下,我们使用互联网连接,保存该连接的值,并相应地更新UI组件。

这里有一个简单的场景,可以帮助你理解LiveData的概念以及它对UI组件的作用。让我们以YouTube应用为例。当你打开你的YouTube应用时,突然网络连接中断,该应用立即更新UI组件并显示你没有网络连接。

youtube-offline

而当你的网络连接恢复后,用户界面会立即更新新的状态,显示连接现在是活动的。

youtube-online

连接管理器

ConnectivityManager() 用于最初注册一个特定的网络请求。它是一个安卓类,提供了一个关于无线连接状态的所有当前问题的列表。它通过在网络连接状态得到改变时通知他们来做到这一点。

它做了以下工作。

  • 获取关于你所连接的网络的信息。
  • 监视网络连接的类型,如Wi-Fi、蜂窝数据等。
  • 当网络连接丢失或改变时,发送广播意图。这在网络连接改变或失败时通知应用程序。
  • 当连接丢失时,尝试 "故障转移 "网络。
  • 设置一个API,允许应用程序查询可用的网络。它使用这个API来监控和报告网络连接。

我们将创建一个可以输出LiveData对象的类。这个类将输出一个布尔值,表示你的应用程序内是否有网络连接,这取决于连接性管理器返回的信息。

设置一个安卓项目

前往你的Android Studio,创建一个新的空活动项目。在这样做的时候,记住我们正在使用Kotlin。所以要确保它被选中。

create-a-kotlin-app

我们需要访问互联网连接属性,所以我们必须添加访问网络状态的权限。

前往你的应用程序的Manifest文件,添加以下内容。

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

然后创建一个新的Kotlin类,我们将用它来处理LiveData和连接管理器。

为这个类命名LiveDataInternetConnections

设置LiveData和ConnectivityManager

我们上面创建的类将接收ConnectivityManager()LiveData

class LiveDataInternetConnections(private val connectivityManager: ConnectivityManager):
LiveData<Boolean>(){

   constructor(appContext: Application) : this(
     appContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
   )
}

我们还将得到一个对ConnectivityManager() 的引用,该引用将用于最初注册一个特定的网络请求和访问网络状态所需的回调。

这是帮助我们获得CONNECTIVITY SERVICES 。你需要获得一个ConnectivityManager() 对象并传入appContext.getSystemService() 。然后传入Context 常量CONNECTIVITY_SERVICE ,并将其转换为ConnectivityManager

设置NetworkCallback

当你想监视网络的变化时,你需要创建一个回调并在ConnectivityManager() 上注册一个网络回调。

这里我们将创建一个NetworkCallback() 。这是一个基类,用于设置NetworkRequest 回调。

这个回调是通过创建一个扩展了NetworkCallback() 类的对象来创建的,该对象来自于ConnectivityManager()

这个对象将使我们有可能获得有用的信息。例如,只要网络变得可用,onAvailable() 方法就会被调用。而一旦我们失去与这种网络的连接,onLost() 方法就会被调用。

这样的方法带有默认的旋钮实现。当你即将失去对网络的把手时,你可能需要得到警告。在这种情况下,onLosing() 方法将被调用,通知你还有多少时间可以使用这个连接。你可以自由地实现你在特定情况下的需要。

在这个例子中,我们将首先添加onAvailable()onLost() 方法,如下所示。

override fun onAvailable(network: Network) {
   super.onAvailable(network)
   Log.d(ContentValues.TAG, "onAvailable: Network ${network} is Available")
   postValue(true)
}

override fun onLost(network: Network) {
   super.onLost(network)
   Log.d(ContentValues.TAG, "onLost: ${network} Network Lost") 
   postValue(false)
}

由于我们正在扩展LiveData类,我们就可以调用postValue() 方法。这需要一个布尔值。当连接可用时,true 值将被添加到LiveData ,否则false 。然后我们可以附加一个观察器,它将根据当前值更新UI组件。

注册和取消注册一个NetworkCallback

为了使用ConnectivityManager() 属性,你必须注册一个回调。在设置了NetworkCallback() ,我们需要根据活动观察者来设置何时注册和取消注册该回调。

这样,我们只在需要的时候注册一个回调,在不再需要的时候取消注册。这样的回调会从请求中移除,这样我们就不会浪费任何资源。

这里我们将有两个覆盖函数,onActive()onInactive() ,如下所示。

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onActive() {
   super.onActive()
   val builder = NetworkRequest.Builder()
   connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onInactive() {
   super.onInactive()
   connectivityManager.unregisterNetworkCallback(networkCallback)
}

我们首先使用Builder 建立网络请求对象。然后在onActive() 函数中注册我们的网络回调。一旦没有更多的观察者,onInactive() 就会被调用,我们就会取消对回调的注册。

如果回调是通过registerNetworkCallback() 注册的,那么它将被调用,用于每个不再满足回调条件的网络。

设置观察者

我们现在有一个类,它将用互联网连接状态更新LiveData对象。现在让我们来设置观察者/UI元素,它们将根据所请求的NetworkCallback() 返回的当前值进行相应的更新。

这里我们将使用XML 代码来设置UI。

在你的activity_main.xml 文件中添加以下代码。我们正在添加两个不可见的文本视图,它们将根据可用连接返回的值进行更新。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:background="#FAFAFA"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <TextView
      android:id="@+id/connected"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="#0B0A0A"
      android:gravity="center"
      android:text="Connected"
      android:textColor="#ff669900"
      android:textSize="30dp"
      android:textStyle="bold"
      app:layout_constraintBottom_toBottomOf="parent"
      android:visibility="gone"
      tools:ignore="MissingConstraints"
      tools:layout_editor_absoluteX="0dp"
      tools:layout_editor_absoluteY="699dp" />

   <TextView
      android:id="@+id/not_connected"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="#0B0A0A"
      android:gravity="center"
      android:text="No Connection"
      android:textColor="#ffcc0000"
      android:textSize="30dp"
      app:layout_constraintBottom_toBottomOf="parent"
      android:textStyle="bold"
      android:visibility="gone"
      tools:ignore="MissingConstraints"
      tools:layout_editor_absoluteX="0dp"
      tools:layout_editor_absoluteY="699dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

在你的onCreate() 方法上面初始化这两个TextViews。

private lateinit var connected : TextView
private lateinit var not_connected : TextView

现在让我们给这些元素附加一个观察者。

TextView1 = findViewById(R.id.connected)
TextView2 = findViewById(R.id.not_connected)

val cld = LiveDataInternetConnections(application)

cld.observe(this, { isConnected ->
   if (isConnected) {
     TextView1.visibility = View.VISIBLE
     TextView2.visibility = View.GONE
   }else{
     TextView1.visibility = View.GONE
     TextView2.visibility = View.VISIBLE
   }
})

从上面的代码中,我们已经实例化了我们创建的用于检查和保持网络值的类。在这里,我们将这些值观察为truefalse

在这种情况下,观察者将检查当前保存的值并更新UI元素。你可以选择任何适合你的应用程序的UI组件,如Toast消息、AlertDialogs、SnackBars等。

我们已经将LiveDataInternetConnections ,即cld.observe(this, { isConnected -> }) ,附在这个观察者身上。

每当ConnectivityManager() 返回为true 的值时,LiveData类保存的值将被返回并保存为isConnected

然后我们在观察isConnected 的值。当trueTextView1 应该总是可见的,而TextView2 不应该可见,反之亦然。

测试该应用程序

这个应用程序现在已经准备好进行测试了。你可以在真实的设备上或模拟器上运行它。

connected

一旦你关闭了你的Wi-Fi或蜂窝数据,应用程序就会被更新,一个 "未连接 "的TextView就会变得可见。

no-connections

然而,如果你的蜂窝数据或Wi-Fi是激活的,但没有激活的网络连接,它仍然会显示你连接到了一个网络。

这就产生了一个限制,因为即使网络没有互联网,它也显示为连接。我们只是检测是否有一个可用的网络,以及是否连接到该网络。

因此,你需要检测网络,检测你何时连接到一个网络,并测试该网络是否真的有互联网。现在让我们在这个应用的基础上,检查网络是否有效,是否具有连接能力。

检查网络能力

我们将添加一个额外的检查,以验证连接的有效性。在onAvailable()onLost() 方法的基础上,我们将添加onCapabilitiesChanged() 方法。就在onAvailable() 和下面的onCapabilitiesChanged() 检查。

@RequiresApi(Build.VERSION_CODES.M)
override fun onCapabilitiesChanged(
   network: Network,
   networkCapabilities: NetworkCapabilities) {
   val isInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)

   Log.d(ContentValues.TAG, "networkCapabilities: ${network} $networkCapabilities")

   val isValidated = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)

   if (isValidated){
     Log.d(ContentValues.TAG, "hasCapability: ${network} $networkCapabilities")
   } else{
     Log.d(ContentValues.TAG, "Network has No Connection Capability: ${network} $networkCapabilities")
   }
   postValue(isInternet && isValidated)
}
  • onCapabilitiesChanged() 检查是否有网络连接。
  • networkCapabilities() 基本上是检查这个网络是启用了Wi-Fi还是蜂窝数据。从技术上讲,有两种类型的网络。它们都可以有互联网,并可以传递给 函数。onAvailable()
  • networkCapabilities() 检查我们所连接的网络的种类。这是通过将其扩展到 ,并添加一个常量 。hasCapability() NET_CAPABILITY_INTERNET

一旦我们检查了我们是连接到Wi-Fi还是蜂窝数据,我们需要验证可用的网络有一个有效的连接。这可以通过将networkCapabilities() 扩展到hasCapability() ,并添加一个常量NET_CAPABILITY_VALIDATED 来完成。根据返回的值,我们将把这些数据保存到postValue()

注意:只有从version Build.VERSION_CODES.O onAvailable() 开始,总是会立即调用onCapabilitiesChanged() 。在低于Build.VERSION_CODES.O 的版本中,当应用程序以互联网连接启动时,除了onAvailable() ,没有任何东西被调用。因此,我们需要在这里传递postValue(true) (尽管在某些情况下,它可能是false 正)。

当使用Builder 构建我们的网络请求对象时,我们也需要添加这些能力。这是我们最感兴趣的网络属性,用于检查网络连接是否有互联网。我们需要在Builder 请求中添加一个常量NET_CAPABILITY_INTERNET

前往onActive() 方法,并按如下方式更新。

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onActive() {
   super.onActive()
   val builder = NetworkRequest.Builder()
   connectivityManager.registerNetworkCallback(builder
     .addCapability(NET_CAPABILITY_INTERNET)
     .build(), networkCallback)
}

测试网络能力

我们将使用LogCat来测试这些功能。运行你的应用程序,如果可能的话,尝试使用一个真实的设备。一旦你运行应用程序,打开你的LogCat,选择正在运行的应用程序,过滤调试日志。

logcat

打开你的应用程序,观察logCat的信息。

network-with-capabilities

如果你在连接到活动的Wi-Fi时打开你的应用程序,TheonAvailable() 方法将被调用。networkCapabilities() 被选中。根据上述logCat消息,我们可以看到这个Wi-Fi有能力连接。

接下来,尝试连接到你知道没有互联网连接的Wi-Fi/蜂窝电话。

network-with-no-capabilities

由于我们连接到了一个网络,onAvailable() 方法将被成功执行,接着是networkCapabilities() ,这表明我们连接到了一个Wi-Fi网络。

然而,这个连接是无效的,它有No Connection Capability 。因此,我们不能使用这个连接访问任何在线数据。而如果你看一下你的应用程序,用户界面已经相应地更新了,有一个No Connection TextView。

最后,如果你完全关闭Wi-Fi,onLost() 方法将被调用。

lost-connection

因为我们没有可用的连接,所以我们没有networkCapabilities() 检查。而当前的LiveData值将被保存为false ,相应地更新UI元素。

总结

这是一个很好的用例,可以告诉你的用户什么时候有网络连接或没有。你可以在你的应用程序中使用它来传递连接性布尔值,并根据是否有网络连接来改变你的UI组件。

ConnectivityManager() 的概念有点宽泛,还有更多的方法,你可以根据你希望你的应用程序实现的目标来使用。