凌峰创科服务平台

Android开发如何连接服务器?

目录

  1. 核心概念:客户端-服务器模型
  2. Android 网络权限配置
  3. Android 网络请求的限制(主线程问题)
  4. 主流的连接方式与库
    • HttpURLConnection (原生、轻量)
    • OkHttp (现代、高效、推荐)
    • Retrofit (类型安全、优雅、推荐)
  5. 数据交换格式:JSON
  6. 实战演练:使用 OkHttp 和 Retrofit 连接一个 RESTful API
  7. 高级主题
    • HTTPS 与网络安全
    • 处理身份验证
    • 连接状态管理
  8. 总结与学习路径

核心概念:客户端-服务器模型

想象一下你去餐厅吃饭:

Android开发如何连接服务器?-图1
(图片来源网络,侵删)
  • 你的手机 (Android App):是 客户端,它主动发起请求,给我一份菜单”。
  • 餐厅的厨房 (服务器):是 服务器,它接收请求,进行处理(准备菜单),然后将结果(菜单)返回给你。

在开发中,这个过程是通过 HTTP/HTTPS 协议实现的,客户端发送一个 HTTP 请求,服务器处理后返回一个 HTTP 响应

常见的 HTTP 请求方法:

  • GET:获取数据(如获取用户列表)。
  • POST:提交数据(如注册新用户)。
  • PUT:更新数据(如修改用户信息)。
  • DELETE:删除数据(如删除一条帖子)。

Android 网络权限配置

你的 App 需要明确告诉操作系统:“我需要访问网络!”,这通过在 AndroidManifest.xml 文件中添加 <uses-permission> 标签来实现。

<!-- 在 <manifest> 标签内添加 -->
<uses-permission android:name="android.permission.INTERNET" />

注意:从 Android 9 (API 28) 开始,默认情况下,App 不能使用明文的 HTTP 流量,必须使用 HTTPS,如果你的服务器暂时不支持 HTTPS,你可以在 AndroidManifest.xml<application> 标签中添加 android:usesCleartextTraffic="true" 来允许(但不推荐,仅用于开发测试)。

Android开发如何连接服务器?-图2
(图片来源网络,侵删)
<!-- 在 <application> 标签内添加 -->
<application
    ...
    android:usesCleartextTraffic="true">
    ...
</application>

Android 网络请求的限制(主线程问题)

这是 Android 开发中最重要的规则之一!

为了保持用户界面的流畅,Android 的 UI 操作(如更新按钮文字、显示列表)必须在 主线程(或 UI 线程)上执行。

网络请求是一个耗时操作,它需要等待服务器响应,这个过程可能从几百毫秒到几秒不等,如果在主线程上执行网络请求,你的 App 的 UI 会被“卡住”,系统会抛出一个 NetworkOnMainThreadException 异常,甚至可能导致系统弹出“应用无响应”(ANR) 的对话框。

解决方案:所有的网络请求都必须在 后台线程 中执行。

Android开发如何连接服务器?-图3
(图片来源网络,侵删)

主流的连接方式与库

在 Android 中,你有以下几种方式来执行网络请求:

HttpURLConnection (原生、轻量)

这是 Android SDK 自带的 API,无需添加第三方库,它功能全面,但 API 相对繁琐,需要手动处理线程、输入输出流等。

适用场景:对于非常简单的 App,或者想不依赖第三方库的场景。

代码示例 (Kotlin)

// 必须在后台线程执行
Thread {
    val url = URL("https://api.example.com/users")
    val urlConnection = url.openConnection() as HttpURLConnection
    try {
        urlConnection.requestMethod = "GET"
        urlConnection.connectTimeout = 5000 // 5秒连接超时
        urlConnection.readTimeout = 5000   // 5秒读取超时
        val responseCode = urlConnection.responseCode
        if (responseCode == HttpURLConnection.HTTP_OK) {
            val inputStream = urlConnection.inputStream
            val response = inputStream.bufferedReader().use { it.readText() }
            // 在这里处理 response,例如通过 Handler 更新 UI
            Log.d("Network", "Response: $response")
        } else {
            Log.e("Network", "Error: HTTP $responseCode")
        }
    } finally {
        urlConnection.disconnect()
    }
}.start()

缺点:代码冗长,需要自己管理线程,处理 JSON 等也比较麻烦。


OkHttp (现代、高效、推荐)

OkHttp 是目前最流行的 HTTP 客户端之一,它由 Square 公司(也是 Retrofit 的开发者)维护,以其高效、简洁和强大的功能而闻名。

优点

  • 支持同步和异步请求。
  • 内置连接池,复用 TCP 连接,性能优异。
  • 支持 GZIP 压缩,减少数据传输量。
  • 简单易用的拦截器机制,可以统一处理日志、缓存等。

添加依赖

// build.gradle (Module :app)
dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
}

代码示例 (Kotlin)

// 1. 创建 OkHttpClient 实例(通常作为单例)
val client = OkHttpClient()
// 2. 创建一个 Request
val request = Request.Builder()
    .url("https://api.example.com/users")
    .get()
    .build()
// 3. 异步执行请求(推荐)
client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        // 请求失败,例如网络错误、超时
        e.printStackTrace()
        // 在这里更新 UI,提示用户网络错误
    }
    override fun onResponse(call: Call, response: Response) {
        // 请求成功
        response.use { // use 确保response.body()被关闭
            if (it.isSuccessful) {
                val responseBody = it.body?.string()
                // 在这里处理 responseBody,并通过 Handler 或 LiveData 更新 UI
                Log.d("OkHttp", "Response: $responseBody")
            } else {
                // 服务器返回了错误状态码,如 404, 500
                Log.e("OkHttp", "Error: ${it.code}")
            }
        }
    }
})

OkHttp 已经帮你处理了线程问题,enqueue 方法会自动在后台线程执行请求,并通过回调将结果返回。


Retrofit (类型安全、优雅、推荐)

Retrofit 本身不执行网络请求,它是一个 类型安全的 HTTP 客户端,它的核心作用是将一个 HTTP API 的接口转换成一个可调用的 Java/Kotlin 对象,它内部默认使用 OkHttp 来执行网络请求。

优点

  • 接口定义:通过定义接口来描述 API,代码清晰、易于维护。
  • 类型安全:自动将 JSON 响应体映射到 Kotlin/Java 对象,无需手动解析。
  • 支持多种适配器:可以轻松切换为 RxJava、Kotlin Coroutines 等异步处理方式。
  • 强大的转换器:支持 JSON、XML、Protobuf 等多种数据格式。

添加依赖

// build.gradle (Module :app)
dependencies {
    // 1. Retrofit 核心库
    implementation("com.squareup.retrofit2:retrofit:2.9.0") // 使用最新版本
    // 2. JSON 转换器 (Gson 是最常用的)
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    // 3. 如果想使用 Coroutines (推荐)
    implementation("com.squareup.retrofit2:converter-scalars:2.9.0") // 可选,用于处理原始字符串
    implementation("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2") // 可选,旧版本
    // 新版本 Coroutines Adapter 可能需要不同的配置,通常直接使用 suspend 函数即可
}

实战演练

假设服务器 API 是 https://api.example.com/users,返回一个 JSON 数组。

定义数据模型

// User.kt
data class User(
    val id: Int,
    val name: String,
    val email: String
)

定义 API 接口

// ApiService.kt
import retrofit2.http.GET
interface ApiService {
    @GET("users")
    suspend fun getUsers(): List<User> // 使用 suspend 函数,可以在协程中直接调用
}

创建 Retrofit 实例

// RetrofitClient.kt (通常是一个单例对象)
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
    private const val BASE_URL = "https://api.example.com/"
    val instance: ApiService by lazy {
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
            .build()
        retrofit.create(ApiService::class.java)
    }
}

在 ViewModel 或协程中调用

// 在 ViewModel 或 Activity/Fragment 的协程作用域中调用
// 在 ViewModel 中
class MyViewModel : ViewModel() {
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
    fun fetchUsers() {
        viewModelScope.launch { // viewModelScope 会自动在配置更改时取消
            try {
                val userList = RetrofitClient.instance.getUsers()
                _users.postValue(userList) // postValue 用于在后台线程更新 LiveData
            } catch (e: Exception) {
                // 处理网络错误或解析错误
                Log.e("MyViewModel", "Failed to fetch users", e)
            }
        }
    }
}

Retrofit + OkHttp + Coroutines 是目前 Android 官方推荐和社区最主流的组合,代码非常简洁和现代化。


数据交换格式:JSON

服务器和客户端之间通常使用 JSON (JavaScript Object Notation) 格式来交换数据,因为它轻量、易于人阅读,并且可以被大多数编程语言轻松解析。

  • 服务器响应[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
  • 客户端模型List<User> (如上例所示)

Retrofit 的 Gson/Jackson 转换器会自动完成 JSON 字符串到 Kotlin 对象的转换。


实战演练:使用 OkHttp 和 Retrofit 连接一个 RESTful API

假设我们要连接一个公共的测试 API:JSONPlaceholder

目标:获取所有 Todo 列表。

添加依赖 (OkHttp + Retrofit + Gson)。

定义数据模型

// Todo.kt
data class Todo(
    val userId: Int,
    val id: Int,
    val title: String,
    val completed: Boolean
)

定义 API 接口

// TodoApiService.kt
import retrofit2.http.GET
interface TodoApiService {
    @GET("todos")
    suspend fun getTodos(): List<Todo>
}

创建 Retrofit 客户端

// RetrofitClient.kt
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
    private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
    val instance: TodoApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(TodoApiService::class.java)
    }
}

在 ViewModel 中调用

// TodoViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class TodoViewModel : ViewModel() {
    private val _todos = MutableLiveData<List<Todo>>()
    val todos: LiveData<List<Todo>> = _todos
    fun loadTodos() {
        viewModelScope.launch {
            try {
                val fetchedTodos = RetrofitClient.instance.getTodos()
                _todos.postValue(fetchedTodos)
            } catch (e: Exception) {
                // 处理错误
                _todos.postValue(emptyList()) // 可以设置为空列表或显示错误状态
                e.printStackTrace()
            }
        }
    }
}

在 UI 中观察数据并显示 (例如使用 Jetpack Compose):

// TodoScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun TodoScreen(todoViewModel: TodoViewModel = viewModel()) {
    // 当进入 Composable 时,触发数据加载
    LaunchedEffect(key1 = true) {
        todoViewModel.loadTodos()
    }
    val todos by todoViewModel.todos.collectAsState()
    if (todos.isEmpty()) {
        // 可以显示一个加载指示器
        Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
            CircularProgressIndicator()
        }
    } else {
        Column(modifier = Modifier.padding(16.dp)) {
            Text("Todo List", style = MaterialTheme.typography.headlineMedium)
            Spacer(modifier = Modifier.height(8.dp))
            todos.forEach { todo ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp)
                ) {
                    Text(
                        text = "${todo.id}: ${todo.title}",
                        modifier = Modifier.padding(16.dp)
                    )
                }
            }
        }
    }
}

高级主题

HTTPS 与网络安全

  • 为什么必须用 HTTPS?
    • 加密:防止数据在传输过程中被窃听。
    • 身份验证:确保你连接的是真正的服务器,而不是中间人攻击。
  • 如何配置?
    • 服务器端:你的服务器必须配置 SSL 证书(可以是 Let's Encrypt 的免费证书)。
    • 客户端 (Android):现代 Android 版本默认只信任受信任的 CA (证书颁发机构) 签发的证书,如果你的服务器使用自签名证书,你需要在 App 中进行额外的信任配置,这通常不推荐,因为会降低安全性。

处理身份验证

很多 API 需要身份验证才能访问,常见方式:

  • API Key:通常放在请求头中。
    // 在 Retrofit 接口中
    @GET("users")
    suspend fun getUsers(
        @Header("Authorization") apiKey: String // 或者 @HeaderMap
    ): List<User>
  • Bearer Token (JWT):最流行的现代方式,也放在请求头中。
    @GET("profile")
    suspend fun getProfile(
        @Header("Authorization") token: String // "Bearer your_token_here"
    ): UserProfile
  • OAuth 2.0:更复杂的流程,通常用于第三方登录(如 Google, Facebook)。

连接状态管理

一个好的 App 应该能感知网络状态。

  • 显示加载指示器:当请求正在进行时,显示一个进度条或旋转动画。
  • 处理无网络情况:检测到网络不可用时,禁用相关操作,并提示用户。
  • 缓存策略:使用 OkHttp 的缓存机制,让用户在没有网络时也能查看上次加载的数据。

总结与学习路径

方式 优点 缺点 适用场景
HttpURLConnection 原生,无需依赖 API繁琐,需手动管理线程 简单工具类、学习原理
OkHttp 高效、简洁、功能强大 需要手动解析JSON 大多数网络请求,作为Retrofit的基础
Retrofit 类型安全、接口化、易于维护 需要额外学习成本 强烈推荐,所有需要与服务器交互的现代App

推荐学习路径

  1. 理解基础:先搞懂 HTTP 协议、GET/POST 请求、JSON 格式和 Android 的主线程限制。
  2. 从 OkHttp 开始:尝试使用 OkHttp 发送一个简单的 GET 请求,感受一下异步回调的机制。
  3. 拥抱 Retrofit:学习如何定义 API 接口,并结合数据模型和 JSON 转换器,这是你未来开发的主力工具。
  4. 结合现代架构:将 Retrofit 与 ViewModelLiveData/StateFlowKotlin Coroutines 结合起来,构建一个响应式、健壮的网络层。
  5. 深入高级特性:学习 OkHttp 的拦截器、Retrofit 的适配器(如 RxJava)以及如何处理复杂的认证和错误。

希望这份详细的指南能帮助你顺利地在 Android 开发中连接服务器!祝你编码愉快!

分享:
扫描分享到社交APP
上一篇
下一篇