连接到网络
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
若要在您的应用中执行网络操作,您的清单必须包含以下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
安全网络通信的最佳实践
在向应用添加网络功能前,您需要确保应用内的数据和信息在通过网络传输时始终安全。为此,请遵循以下网络安全最佳实践:
- 尽量减少通过网络传输的敏感或个人用户数据的数量。
- 来自应用的所有网络流量均通过 SSL 发送。
- 考虑创建网络安全配置,让应用信任自定义证书授权机构 (CA) 或限制应用信任的一组系统 CA 以确保安全通信。
如需详细了解网络的安全使用原则,请参阅网络安全提示。
选择 HTTP 客户端
大多数联网应用都使用 HTTP 来发送和接收数据。Android 平台包含 HttpsURLConnection
客户端,该客户端支持传输层安全协议 (TLS)、流式上传与下载、可配置超时、IPv6 以及连接池。
此外,某些第三方库也提供更高级别的网络操作 API。这些库支持各种便捷功能,例如对请求正文进行序列化,以及对响应正文进行反序列化。
- Retrofit:来自 Square 的 JVM 的类型安全 HTTP 客户端,基于 OkHttp 构建。Retrofit 可让您以声明方式创建客户端接口,并支持多个序列化库。
- Ktor:来自 JetBrains 的 HTTP 客户端,完全针对 Kotlin 构建,并由协程提供支持。Ktor 支持各种引擎、序列化器和平台。
解析 DNS 查询
搭载 Android 10(API 级别 29)及更高版本的设备通过明文查找和“通过 TLS 执行 DNS”模式对专用 DNS 查找提供内置支持。DnsResolver
API 提供通用的异步解析,使您能够查找 SRV
、NAPTR
和其他记录类型。解析响应由应用负责执行。
在搭载 Android 9(API 级别 28)及更低版本的设备上,平台 DNS 解析器仅支持 A
和 AAAA
记录。这允许您查找与名称关联的 IP 地址,但不支持任何其他记录类型。
对于基于 NDK 的应用,请参阅 android_res_nsend
。
使用存储区封装网络操作
为了简化执行网络操作的流程,并减少应用各个部分中的代码重复,您可以使用存储区设计模式。存储区是用于处理数据操作并针对某些特定数据或资源提供干净的 API 抽象的类。
您可以使用 Retrofit 声明一个用于为网络操作指定 HTTP 方法、网址、参数和响应类型的接口,如以下示例所示:
Kotlin
interface UserService {
@GET("/users/{id}")
suspend fun getUser(@Path("id") id: String): User
}
Java
public interface UserService {
@GET("/user/{id}")
Call<User> getUserById(@Path("id") String id);
}
在存储区类中,函数可以封装网络操作并公开其结果。这种封装可确保调用仓库的组件不需要知道数据的存储方式。数据存储方式将来发生的任何变化也只与仓库类相关。例如,您可能要进行远程更改(如更新 API 端点),或者您可能需要实现本地缓存。
Kotlin
class UserRepository constructor(
private val userService: UserService
) {
suspend fun getUserById(id: String): User {
return userService.getUser(id)
}
}
Java
class UserRepository {
private UserService userService;
public UserRepository(
UserService userService
) {
this.userService = userService;
}
public Call<User> getUserById(String id) {
return userService.getUser(id);
}
}
为了避免创建无响应的界面,请勿在主线程上执行网络操作。默认情况下,Android 要求在非主界面线程上执行网络操作。如果您尝试在主线程上执行网络操作,系统会抛出 NetworkOnMainThreadException
。
在上一个代码示例中,实际并未触发网络操作。UserRepository
的调用方必须使用协程或使用 enqueue()
函数来实现线程处理。如需了解详情,请参阅从互联网获取数据 Codelab,其中演示了如何使用 Kotlin 协程实现线程处理。
让 activity 在配置变更后继续存在
当发生配置更改(例如屏幕旋转)时,您的 fragment 或 activity 会被销毁并重新创建。所有未在您的 fragment 或 activity 的实例状态(只能存储少量数据)中保存的数据都会丢失。如果发生这种情况,您可能需要再次发出网络请求。
您可以使用 ViewModel
让数据在配置更改后仍然保留。ViewModel
组件旨在以注重生命周期的方式存储和管理界面相关的数据。使用前面的 UserRepository
时,ViewModel
可以发出必要的网络请求,并使用 LiveData
向您的 fragment 或 activity 提供结果:
Kotlin
class MainViewModel constructor(
savedStateHandle: SavedStateHandle,
userRepository: UserRepository
) : ViewModel() {
private val userId: String = savedStateHandle["uid"] ?:
throw IllegalArgumentException("Missing user ID")
private val _user = MutableLiveData<User>()
val user = _user as LiveData<User>
init {
viewModelScope.launch {
try {
// Calling the repository is safe as it moves execution off
// the main thread
val user = userRepository.getUserById(userId)
_user.value = user
} catch (error: Exception) {
// Show error message to user
}
}
}
}
Java
class MainViewModel extends ViewModel {
private final MutableLiveData<User> _user = new MutableLiveData<>();
LiveData<User> user = (LiveData<User>) _user;
public MainViewModel(
SavedStateHandle savedStateHandle,
UserRepository userRepository
) {
String userId = savedStateHandle.get("uid");
Call<User> userCall = userRepository.getUserById(userId);
userCall.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
_user.setValue(response.body());
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// Show error message to user
}
});
}
}
如需详细了解此主题,请参阅以下相关指南:
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-08-21。
[null,null,["最后更新时间 (UTC):2025-08-21。"],[],[],null,["# Connect to the network\n\nTo perform network operations in your application, your manifest must include\nthe following permissions: \n\n \u003cuses-permission android:name=\"android.permission.INTERNET\" /\u003e\n \u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /\u003e\n\n| **Note:** Both the [`INTERNET`](/reference/android/Manifest.permission#INTERNET) and [`ACCESS_NETWORK_STATE`](/reference/android/Manifest.permission#ACCESS_NETWORK_STATE) permissions are [normal\n| permissions](/develop/permissions/overview#normal-dangerous), which means they're granted at install time and don't need to be [requested at runtime](/training/permissions/requesting).\n\nBest practices for secure network communication\n-----------------------------------------------\n\nBefore you add networking functionality to your app, you need to ensure that\ndata and information within your app stay safe when you are transmitting over a\nnetwork. To do so, follow these networking security best practices:\n\n- Minimize the amount of sensitive or personal [user\n data](/privacy-and-security/security-tips#user-data) that you transmit over the network.\n- Send all network traffic from your app over [SSL](/training/articles/security-ssl).\n- Consider creating a [network security\n configuration](/training/articles/security-config), which lets your app trust custom certificate authorities (CAs) or restrict the set of system CAs that it trusts for secure communication.\n\nFor more information on how to apply secure networking principles, see the\n[networking security tips](/privacy-and-security/security-tips#networking).\n\nChoose an HTTP client\n---------------------\n\nMost network-connected apps use HTTP to send and receive data. The Android\nplatform includes the\n[`HttpsURLConnection`](/reference/javax/net/ssl/HttpsURLConnection) client,\nwhich supports TLS, streaming uploads and downloads, configurable timeouts,\nIPv6, and connection pooling.\n\nThird-party libraries that offer higher-level APIs for networking operations are\nalso available. These support various convenience features, such as the\nserialization of request bodies and deserialization of response bodies.\n\n- [Retrofit](https://square.github.io/retrofit/): a type-safe HTTP client for the JVM from Square, built on top of OkHttp. Retrofit lets you create a client interface declaratively and has support for several serialization libraries.\n- [Ktor](https://ktor.io/): an HTTP client from JetBrains, built entirely for Kotlin and powered by coroutines. Ktor supports various engines, serializers, and platforms.\n\nResolve DNS queries\n-------------------\n\nDevices that run Android 10 (API level 29) and higher have built-in support for\nspecialized DNS lookups through both cleartext lookups and DNS-over-TLS mode.\nThe [`DnsResolver`](/reference/android/net/DnsResolver) API provides generic,\nasynchronous resolution, which lets you look up `SRV`, `NAPTR`, and other\nrecord types. Parsing the response is left to the app to perform.\n\nOn devices that run Android 9 (API level 28) and lower, the platform DNS\nresolver supports only `A` and `AAAA` records. This lets you look up the IP\naddresses associated with a name but doesn't support any other record types.\n\nFor NDK-based apps, see\n[`android_res_nsend`](/ndk/reference/group/networking#android_res_nsend).\n\nEncapsulate network operations with a repository\n------------------------------------------------\n\nTo simplify the process of performing network operations and reduce code\nduplication in various parts of your app, you can use the repository design\npattern. A repository is a class that handles data operations and provides a\nclean API abstraction over some specific data or resource.\n\nYou can use Retrofit to declare an interface that specifies the HTTP method,\nURL, arguments, and response type for network operations, as in the following\nexample: \n\n### Kotlin\n\n```kotlin\ninterface UserService {\n @GET(\"/users/{id}\")\n suspend fun getUser(@Path(\"id\") id: String): User\n}\n```\n\n### Java\n\n```java\npublic interface UserService {\n @GET(\"/user/{id}\")\n Call\u003cUser\u003e getUserById(@Path(\"id\") String id);\n}\n```\n\nWithin a repository class, functions can encapsulate network operations and\nexpose their results. This encapsulation ensures that the components that call\nthe repository don't need to know how the data is stored. Any future changes to\nhow the data is stored are isolated to the repository class as well. For\nexample, you might have a remote change such as an update to API endpoints, or\nyou might want to implement local caching. \n\n### Kotlin\n\n```kotlin\nclass UserRepository constructor(\n private val userService: UserService\n) {\n suspend fun getUserById(id: String): User {\n return userService.getUser(id)\n }\n}\n```\n\n### Java\n\n```java\nclass UserRepository {\n private UserService userService;\n\n public UserRepository(\n UserService userService\n ) {\n this.userService = userService;\n }\n\n public Call\u003cUser\u003e getUserById(String id) {\n return userService.getUser(id);\n }\n}\n```\n\nTo avoid creating an unresponsive UI, don't perform network operations on the\nmain thread. By default, Android requires you to perform network operations on a\nthread other than the main UI thread. If you try to perform network operations\non the main thread, a\n[`NetworkOnMainThreadException`](/reference/android/os/NetworkOnMainThreadException)\nis thrown.\n\nIn the previous code example, the\nnetwork operation isn't actually triggered. The caller of the `UserRepository`\nmust implement the threading either using coroutines or using the `enqueue()`\nfunction. For more information, see the codelab [Get data from the\ninternet](/codelabs/basic-android-kotlin-training-getting-data-internet),\nwhich demonstrates how to implement threading using Kotlin coroutines.\n\nSurvive configuration changes\n-----------------------------\n\nWhen a configuration change occurs, such as a screen rotation, your fragment or\nactivity is destroyed and recreated. Any data not saved in the instance\nstate for your fragment activity, which can only hold small amounts of data,\nis lost. If this occurs, you might need to make your network requests again.\n\nYou can use a [`ViewModel`](/topic/libraries/architecture/viewmodel) to let\nyour data survive configuration changes. The `ViewModel` component is\ndesigned to store and manage UI-related data in a lifecycle-conscious\nway. Using the preceding `UserRepository`, the `ViewModel` can make the\nnecessary network requests and provide the result to your fragment or activity\nusing [`LiveData`](/topic/libraries/architecture/livedata): \n\n### Kotlin\n\n```kotlin\nclass MainViewModel constructor(\n savedStateHandle: SavedStateHandle,\n userRepository: UserRepository\n) : ViewModel() {\n private val userId: String = savedStateHandle[\"uid\"] ?:\n throw IllegalArgumentException(\"Missing user ID\")\n\n private val _user = MutableLiveData\u003cUser\u003e()\n val user = _user as LiveData\u003cUser\u003e\n\n init {\n viewModelScope.launch {\n try {\n // Calling the repository is safe as it moves execution off\n // the main thread\n val user = userRepository.getUserById(userId)\n _user.value = user\n } catch (error: Exception) {\n // Show error message to user\n }\n\n }\n }\n}\n```\n\n### Java\n\n```java\nclass MainViewModel extends ViewModel {\n\n private final MutableLiveData\u003cUser\u003e _user = new MutableLiveData\u003c\u003e();\n LiveData\u003cUser\u003e user = (LiveData\u003cUser\u003e) _user;\n\n public MainViewModel(\n SavedStateHandle savedStateHandle,\n UserRepository userRepository\n ) {\n String userId = savedStateHandle.get(\"uid\");\n Call\u003cUser\u003e userCall = userRepository.getUserById(userId);\n userCall.enqueue(new Callback\u003cUser\u003e() {\n @Override\n public void onResponse(Call\u003cUser\u003e call, Response\u003cUser\u003e response) {\n if (response.isSuccessful()) {\n _user.setValue(response.body());\n }\n }\n\n @Override\n public void onFailure(Call\u003cUser\u003e call, Throwable t) {\n // Show error message to user\n }\n });\n }\n}\n```\n\nRead related guides\n-------------------\n\nTo learn more about this topic, see the following related guides:\n\n- [Reduce network battery drain: Overview](/topic/performance/power)\n- [Minimize the effect of regular updates](/training/efficient-downloads)\n- [Web-based content](/guide/webapps)\n- [Application fundamentals](/guide/components/fundamentals)\n- [Guide to app architecture](/jetpack/guide)"]]