凌峰创科服务平台

Android如何POST数据到服务器?

  1. 核心概念:POST 请求是什么,为什么用它。
  2. 现代方法 (推荐):使用 Retrofit + OkHttp,这是目前 Android 开发的主流和最佳实践。
  3. 传统方法:使用 HttpURLConnection,这是 Android SDK 自带的,无需额外依赖,适合学习或简单场景。
  4. 发送 JSON 数据:这是最常见的服务器交互格式。
  5. 发送表单数据:如 key-value 对。
  6. 重要注意事项:网络权限、子线程、错误处理等。

核心概念:POST 请求

与 GET 请求相比,POST 请求有以下几个关键特点:

Android如何POST数据到服务器?-图1
(图片来源网络,侵删)
  • 数据在请求体中:POST 请求将数据放在 HTTP 请求的 Body 部分,而不是像 GET 一样放在 URL 的查询参数中。
  • 数据大小限制:理论上可以发送非常大的数据,因为它不受 URL 长度的限制。
  • 相对安全:数据不会出现在 URL 或服务器日志中,比 GET 请求更安全。
  • 用途:常用于提交表单、上传文件、创建/更新服务器资源等。

现代方法:使用 Retrofit (强烈推荐)

Retrofit 是一个由 Square 公司开发的类型安全的 HTTP 客户端,它能让你用非常简洁的 Java/Kotlin 接口来定义 API,它会自动帮你实现网络请求的细节,极大地简化了开发。

步骤 1:添加依赖

在你的 app/build.gradle 文件中添加 Retrofit 和 Gson (用于 JSON 解析) 的依赖。

dependencies {
    // Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    // Retrofit 的 Gson 转换器,用于将 JSON 字符串和 Java/Kotlin 对象互相转换
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    // OkHttp (Retrofit 底层默认使用 OkHttp)
    implementation 'com.squareup.okhttp3:okhttp:4.9.3'
    // OkHttp 的日志拦截器,方便调试
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
}

步骤 2:添加网络权限

app/src/main/AndroidManifest.xml 中添加 INTERNET 权限。

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

步骤 3:创建数据模型

创建一个 Kotlin/Java 数据类,其结构与服务器期望接收的 JSON 数据结构一致。

Android如何POST数据到服务器?-图2
(图片来源网络,侵删)

服务器期望的 JSON 是:

{
  "username": "test_user",
  "password": "123456"
}

对应的 Kotlin 数据类:

data class LoginRequest(
    val username: String,
    val password: String
)

步骤 4:创建 API 接口

定义一个接口,用注解来描述 API 的请求方式、URL 和参数。

import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
// 定义一个基础 URL,所有请求都会基于这个 URL
// 你的服务器地址是 http://api.example.com/v2/
const val BASE_URL = "http://your-api-base-url.com/api/"
interface ApiService {
    // @POST: 指定这是一个 POST 请求
    // "login": 请求的相对路径,完整 URL = BASE_URL + "login"
    // @Body: 将整个 LoginRequest 对象作为请求体发送
    @POST("login")
    fun login(@Body loginRequest: LoginRequest): Call<LoginResponse>
}

服务器响应的数据模型:

Android如何POST数据到服务器?-图3
(图片来源网络,侵删)
// 假设服务器返回如下 JSON
// { "code": 200, "message": "登录成功", "token": "xxxxx" }
data class LoginResponse(
    val code: Int,
    val message: String,
    val token: String?
)

步骤 5:创建 Retrofit 实例并发起请求

在你的 ActivityViewModel 中进行网络请求。

重要:网络请求必须在 子线程 中执行! 可以使用协程、RxJava 或 Executor

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val loginButton: Button = findViewById(R.id.login_button)
        loginButton.setOnClickListener {
            // 在真实应用中,用户名和密码应该从输入框获取
            val username = "test_user"
            val password = "123456"
            // 创建 Retrofit 实例
            val retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
                .build()
            // 创建 API 接口的实例
            val apiService = retrofit.create(ApiService::class.java)
            // 创建请求体
            val loginRequest = LoginRequest(username, password)
            // 发起异步请求
            apiService.login(loginRequest).enqueue(object : Callback<LoginResponse> {
                // 请求成功时回调
                override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
                    if (response.isSuccessful) {
                        // response.body() 包含了服务器返回的数据
                        val loginResponse = response.body()
                        loginResponse?.let {
                            Toast.makeText(this@MainActivity, "登录成功: ${it.message}", Toast.LENGTH_SHORT).show()
                            // 在这里处理 token,例如保存到 SharedPreferences
                        }
                    } else {
                        // 服务器返回了错误状态码,如 404, 500 等
                        Toast.makeText(this@MainActivity, "登录失败: ${response.code()}", Toast.LENGTH_SHORT).show()
                    }
                }
                // 请求失败时回调(如网络连接问题)
                override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
                    Toast.makeText(this@MainActivity, "网络错误: ${t.message}", Toast.LENGTH_SHORT).show()
                }
            })
        }
    }
}

传统方法:使用 HttpURLConnection

如果你不想引入第三方库,可以使用 Android 自带的 HttpURLConnection

示例代码 (使用 Kotlin 协程在子线程中执行)

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class HttpURLConnectionActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val postButton: Button = findViewById(R.id.post_button)
        postButton.setOnClickListener {
            // 使用协程在 IO 线程执行网络请求
            CoroutineScope(Dispatchers.IO).launch {
                performPostRequest()
            }
        }
    }
    private fun performPostRequest() {
        val apiUrl = "http://your-api-base-url.com/api/login"
        val jsonPayload = """{"username": "test_user", "password": "123456"}"""
        try {
            val url = URL(apiUrl)
            val urlConnection = url.openConnection() as HttpURLConnection
            urlConnection.requestMethod = "POST" // 设置请求方法为 POST
            urlConnection.setRequestProperty("Content-Type", "application/json; utf-8") // 设置请求头
            urlConnection.setRequestProperty("Accept", "application/json")
            urlConnection.doOutput = true // 允许输出
            // 发送请求体
            OutputStreamWriter(urlConnection.outputStream).use { writer ->
                writer.write(jsonPayload)
                writer.flush()
            }
            // 获取响应码
            val responseCode = urlConnection.responseCode
            if (responseCode == HttpURLConnection.HTTP_OK) { // 200
                // 读取响应数据...
                // val response = urlConnection.inputStream.bufferedReader().use { it.readText() }
                runOnUiThread {
                    Toast.makeText(this@HttpURLConnectionActivity, "POST 请求成功!", Toast.LENGTH_SHORT).show()
                }
            } else {
                runOnUiThread {
                    Toast.makeText(this@HttpURLConnectionActivity, "POST 请求失败,错误码: $responseCode", Toast.LENGTH_SHORT).show()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            runOnUiThread {
                Toast.makeText(this@HttpURLConnectionActivity, "网络异常: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

发送表单数据 (Form-Data)

如果服务器需要接收 application/x-www-form-urlencoded 格式的数据,Retrofit 的实现非常简单。

Retrofit 方式

修改你的 API 接口:

import retrofit2.Call
分享:
扫描分享到社交APP
上一篇
下一篇