凌峰创科服务平台

Android如何向服务器高效发送数据?

客户端-服务器模型

要理解基本的工作流程:

Android如何向服务器高效发送数据?-图1
(图片来源网络,侵删)
  1. Android 客户端:你的 App,负责收集用户数据,并通过网络请求发送给服务器。
  2. 服务器:一个后端服务(通常用 Java, Python, Node.js, PHP 等语言编写),负责接收、处理、存储数据,并可能返回响应。
  3. 网络协议:最常用的是 HTTP/HTTPS,HTTPS 是加密的 HTTP,是现在 App 开发的强制标准,用于保护数据安全。

使用 HttpURLConnection (原生 API)

这是 Android SDK 自带的传统方式,无需引入第三方库,虽然代码稍显繁琐,但它是理解网络请求的基础。

特点:

  • 优点:无需额外依赖,是 Android 平台的一部分。
  • 缺点:API 较为底层,代码冗长,处理异步(在子线程中执行)和回调比较麻烦。

示例:发送 POST 请求(JSON 数据)

假设我们要向 https://api.example.com/users 发送一个 JSON 对象 {"name": "John", "email": "john@example.com"}

添加网络权限

app/src/main/AndroidManifest.xml 中添加:

Android如何向服务器高效发送数据?-图2
(图片来源网络,侵删)
<uses-permission android:name="android.permission.INTERNET" />

在后台线程中执行网络请求

网络操作不能在主线程(UI线程)中执行,否则会抛出 NetworkOnMainThreadException 异常,我们可以使用 Thread + Handler,或者更现代的 Kotlin Coroutines/RxJava,这里展示使用 Thread + Handler 的方式。

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
    private val handler = Handler(Looper.getMainLooper())
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val sendButton: Button = findViewById(R.id.send_button)
        sendButton.setOnClickListener {
            sendDataToServer()
        }
    }
    private fun sendDataToServer() {
        // 在子线程中执行网络请求
        Thread {
            val apiUrl = "https://api.example.com/users"
            val jsonBody = """{"name": "John", "email": "john@example.com"}"""
            try {
                val url = URL(apiUrl)
                val urlConnection = url.openConnection() as HttpURLConnection
                urlConnection.requestMethod = "POST"
                urlConnection.setRequestProperty("Content-Type", "application/json; utf-8")
                urlConnection.setRequestProperty("Accept", "application/json")
                urlConnection.doOutput = true // 允许发送请求体
                // 发送请求体
                DataOutputStream(urlConnection.outputStream).use { os ->
                    os.writeBytes(jsonBody)
                }
                // 获取响应码
                val responseCode = urlConnection.responseCode
                Log.d("Network", "Response Code: $responseCode")
                // 读取响应
                val response = if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) {
                    urlConnection.inputStream.bufferedReader().use { it.readText() }
                } else {
                    urlConnection.errorStream.bufferedReader().use { it.readText() }
                }
                Log.d("Network", "Response: $response")
                // 通过 Handler 切换回主线程更新 UI
                handler.post {
                    Toast.makeText(this@MainActivity, "发送成功! 响应: $response", Toast.LENGTH_LONG).show()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                handler.post {
                    Toast.makeText(this@MainActivity, "发送失败: ${e.message}", Toast.LENGTH_LONG).show()
                }
            }
        }.start()
    }
}

使用第三方库 (强烈推荐)

手动管理 HttpURLConnection 非常繁琐,业界广泛使用第三方库来简化网络请求。

Retrofit + OkHttp (当前业界标准)

这是目前最流行、最强大的组合,Retrofit 负责将 API 接口定义和调用转换成底层的 OkHttp 请求,OkHttp 则是一个高效的 HTTP 客户端。

Android如何向服务器高效发送数据?-图3
(图片来源网络,侵删)

特点

  • 类型安全:通过接口定义 API,编译时检查。
  • 简洁易用:注解驱动的 API 声明,代码非常清晰。
  • 高性能:底层基于 OkHttp,支持异步、同步请求。
  • 可扩展:与 Gson/Moshi (JSON 解析)、RxJava/Kotlin Coroutines (异步处理) 无缝集成。

步骤:

添加依赖

app/build.gradle.kts (或 build.gradle) 中:

// OkHttp (Retrofit 依赖)
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// JSON 转换器 (这里用 Gson)
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// Coroutines 支持 (推荐)
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")

添加网络权限

同上,在 AndroidManifest.xml 中添加 <uses-permission android:name="android.permission.INTERNET" />

创建数据类

// 请求体
data class UserRequest(
    val name: String,
    val email: String
)
// 响应体 (根据服务器返回结构定义)
data class UserResponse(
    val id: String,
    val name: String,
    val email: String,
    val createdAt: String
)

定义 Retrofit API 接口

import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface ApiService {
    @POST("users") // 对应服务器 API 的路径
    fun createUser(@Body userRequest: UserRequest): Call<UserResponse>
}

创建 Retrofit 实例并进行网络调用

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitClient {
    companion object {
        private const val BASE_URL = "https://api.example.com/"
        val instance: ApiService by lazy {
            val retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
            retrofit.create(ApiService::class.java)
        }
    }
}

在 Activity/ViewModel 中调用

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class RetrofitActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_retrofit)
        val sendButton: Button = findViewById(R.id.send_button_retrofit)
        sendButton.setOnClickListener {
            sendPostRequestWithRetrofit()
        }
    }
    private fun sendPostRequestWithRetrofit() {
        val apiService = RetrofitClient.instance
        val userRequest = UserRequest(name = "Jane Doe", email = "jane@example.com")
        // 使用协程进行异步调用 (推荐)
        lifecycleScope.launch {
            try {
                val response = apiService.createUser(userRequest)
                if (response.isSuccessful) {
                    val userResponse = response.body()
                    Log.d("Retrofit", "Success: ${userResponse.toString()}")
                    Toast.makeText(this@RetrofitActivity, "发送成功: ${userResponse?.name}", Toast.LENGTH_SHORT).show()
                } else {
                    Log.e("Retrofit", "Error: ${response.code()} - ${response.message()}")
                    Toast.makeText(this@RetrofitActivity, "发送失败: ${response.message()}", Toast.LENGTH_SHORT).show()
                }
            } catch (e: Exception) {
                Log.e("Retrofit", "Exception: ${e.message}", e)
                Toast.makeText(this@RetrofitActivity, "发送失败: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
        // 或者使用传统的 Callback 方式
        /*
        apiService.createUser(userRequest).enqueue(object : Callback<UserResponse> {
            override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) {
                if (response.isSuccessful) {
                    val userResponse = response.body()
                    Log.d("Retrofit", "Success: ${userResponse.toString()}")
                    Toast.makeText(this@RetrofitActivity, "发送成功: ${userResponse?.name}", Toast.LENGTH_SHORT).show()
                } else {
                    Log.e("Retrofit", "Error: ${response.code()} - ${response.message()}")
                    Toast.makeText(this@RetrofitActivity, "发送失败: ${response.message()}", Toast.LENGTH_SHORT).show()
                }
            }
            override fun onFailure(call: Call<UserResponse>, t: Throwable) {
                Log.e("Retrofit", "Failure: ${t.message}", t)
                Toast.makeText(this@RetrofitActivity, "发送失败: ${t.message}", Toast.LENGTH_SHORT).show()
            }
        })
        */
    }
}

使用 Volley (Google 官方库)

Volley 是 Google 推出的一个网络通信库,特别适合数据量不大、但通信频繁的场景。

特点

  • 优点:自动请求调度,缓存支持,图片加载方便,API 简单。
  • 缺点:对于大文件上传/下载支持不佳,不适合流式数据。

由于 Retrofit 的普及,Volley 现在用得相对少了,但了解它依然有价值。


最佳实践和重要考虑因素

  1. 永远不要在主线程进行网络请求

    • 这会导致应用卡顿,甚至被 Android 系统强制关闭 (ANR - Application Not Responding)。
    • 解决方案:使用 Kotlin CoroutinesRxJavaExecutorService
  2. 使用 HTTPS

    • 所有网络通信都必须使用 HTTPS 协议,以加密数据,防止中间人攻击和窃听。
    • AndroidManifest.xml 中强制使用网络安全配置。
  3. 处理错误和异常

    • 网络是不可靠的,必须处理各种异常情况:
      • SocketTimeoutException:连接超时。
      • UnknownHostException:无法解析域名。
      • SSLHandshakeException:证书验证失败。
    • 同时要处理服务器返回的错误状态码(如 404 Not Found, 500 Internal Server Error)。
  4. 数据格式

    • JSON (JavaScript Object Notation):目前最主流的格式,轻量级、易于人阅读和机器解析,Retrofit + Gson/Moshi 是完美组合。
    • Form-Data:用于传统的表单提交,application/x-www-form-urlencoded
    • Multipart:用于上传文件,multipart/form-data
  5. 安全性

    • 敏感数据:密码、Token 等敏感信息不要通过 GET 请求(URL 参数)发送,必须放在 POST 请求的请求体中。
    • 存储 Token:从服务器获取的认证 Token (如 JWT) 应该安全存储,优先使用 EncryptedSharedPreferences 进行加密存储,而不是普通的 SharedPreferences
  6. 日志

    • 在开发阶段,使用 HttpLoggingInterceptor (OkHttp的一部分) 来打印详细的网络请求和响应日志,非常有助于调试。
    • 在生产环境中,关闭或控制日志级别,避免泄露敏感信息。

总结与选择

特性 HttpURLConnection Volley Retrofit + OkHttp
易用性 差,代码冗长 中等 优秀,注解驱动
性能 一般 良好 优秀,异步高效
功能 基础 内置缓存、图片 高度可扩展,支持多种格式和异步方案
依赖 无需 需要引入 需要引入
推荐场景 简单 demo,不想引入第三方库 小型应用,数据请求频繁但不大 几乎所有现代 Android 应用的首选

对于任何新的 Android 项目,强烈推荐使用 Retrofit + OkHttp 的组合,它代表了当前业界最佳实践,能让你写出更健壮、更易维护、更高效的代码,如果你正在维护一个老项目,并且项目已经使用了 Volley,那么可以继续使用,但在新功能开发时,考虑迁移到 Retrofit。HttpURLConnection 只适合用于学习或极特殊的环境。

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