凌峰创科服务平台

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

目录

  1. 核心概念:客户端-服务器模型
  2. 准备工作:服务器端
  3. Android 客户端发送数据的几种主流方式
    • 使用 HttpURLConnection (原生 Java API,无需第三方库)
    • 使用 OkHttp (强烈推荐,现代 Android 开发标准)
    • 使用 Retrofit (高级 REST API 客户端,基于 OkHttp)
  4. 数据格式选择:JSON vs. Form-Data
  5. 关键注意事项:网络权限、子线程、数据安全
  6. 完整示例:使用 OkHttp 发送 JSON 数据
  7. 进阶话题:文件上传、断点续传、进度监听

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

想象一下你去餐厅点餐:

Android如何高效向服务器发送数据?-图1
(图片来源网络,侵删)
  • 你的手机 (Android Client):是发起请求的一方,它决定要“发送什么数据”(比如点一份宫保鸡丁)。
  • 餐厅厨房 (Server):是接收和处理请求的一方,它根据你的订单(数据)来“做菜”(处理业务逻辑)。
  • 服务员 (HTTP 协议):是你们之间的沟通方式,你通过服务员(POST/GET 请求)把订单(数据)送到厨房,厨房做完菜后,服务员再把结果(响应)端给你。

在技术上,你的手机会向一个特定的网络地址(URL,即餐厅地址)发送一个 HTTP 请求,这个请求包含了:

  • 请求方法GET(获取数据,像看菜单)、POST(提交数据,像下订单)、PUT(更新数据)、DELETE(删除数据)等,我们最常用的是 POST
  • 请求头:一些附加信息,比如内容类型 Content-Type(告诉服务器我发的是什么格式数据)、认证信息等。
  • 请求体:实际要发送的数据内容。

服务器收到请求后,处理数据,然后返回一个 HTTP 响应,包含:

  • 响应状态码200 OK(成功)、404 Not Found(资源不存在)、500 Internal Server Error(服务器内部错误)等。
  • 响应头:服务器返回的附加信息。
  • 响应体:服务器返回的数据内容。

准备工作:服务器端

在写 Android 代码之前,你必须有一个可以接收数据的服务器端,这个服务器端可以是:

  • 你自己用 Node.js, Python (Django/Flask), Java (Spring Boot), PHP 等语言搭建的。
  • 云函数,如阿里云函数计算、腾讯云云函数、AWS Lambda。
  • 第三方 Backend-as-a-Service (BaaS) 平台,如 Firebase, LeanCloud。

为了演示,我们假设一个简单的服务器端 API 端点:

Android如何高效向服务器发送数据?-图2
(图片来源网络,侵删)
  • URL: https://your-api-domain.com/api/users
  • 方法: POST
  • 期望的请求头: Content-Type: application/json
  • 期望的请求体 (JSON):
    {
      "name": "张三",
      "email": "zhangsan@example.com"
    }
  • 成功响应 (JSON):
    {
      "status": "success",
      "message": "User created successfully",
      "user_id": "12345"
    }

Android 客户端发送数据的几种主流方式

使用 HttpURLConnection (原生 Java API)

这是 Android SDK 自带的 API,无需添加任何依赖,但它的代码比较冗长,且功能相对基础。

优点

  • 无需第三方库,减少应用体积。
  • 适用于简单的网络请求。

缺点

  • 代码繁琐,需要手动管理线程、输入输出流。
  • 功能不如第三方库强大(如连接池、拦截器)。

示例代码:

Android如何高效向服务器发送数据?-图3
(图片来源网络,侵删)
// 必须在子线程中执行,或者使用 AsyncTask (已废弃) / Executor
new Thread(new Runnable() {
    @Override
    public void run() {
        String url = "https://your-api-domain.com/api/users";
        try {
            URL urlObj = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
            // 设置请求方法
            connection.setRequestMethod("POST");
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/json");
            // 允许输出请求体
            connection.setDoOutput(true);
            // 构建要发送的 JSON 数据
            String jsonInputString = "{\"name\":\"张三\",\"email\":\"zhangsan@example.com\"}";
            // 写入请求体
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
                os.write(input, 0, input.length);
            }
            // 获取响应码
            int responseCode = connection.getResponseCode();
            System.out.println("Response Code: " + responseCode);
            // 读取响应
            if (responseCode == HttpURLConnection.HTTP_OK) { // success
                BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
                StringBuilder response = new StringBuilder();
                String responseLine;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println("Server Response: " + response.toString());
                // 在这里可以处理服务器返回的数据
            } else {
                System.out.println("POST request failed");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

使用 OkHttp (强烈推荐)

OkHttp 是目前 Android 开发中最流行的网络请求库,它高效、易用,并内置了对 HTTP/2 和 SPDY 的支持。

优点

  • 性能优异,有连接池。
  • API 简洁,易于使用。
  • 支持异步和同步请求。
  • 强大的拦截器机制,可以统一处理日志、认证等。

第一步:添加依赖app/build.gradle 文件的 dependencies 代码块中添加:

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

第二步:添加网络权限app/src/main/AndroidManifest.xml 中添加:

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

如果你的 App 针对 Android 9 (API 28) 或更高版本,默认情况下是明文流量,如果你的 API 是 https,则无需额外配置,如果是 http,需要在 application 标签下添加:

android:usesCleartextTraffic="true"

注意:强烈建议服务器使用 https 协议。

第三步:编写代码

发送同步请求 (在子线程中执行):

// 必须在子线程中执行
new Thread(() -> {
    try {
        String url = "https://your-api-domain.com/api/users";
        OkHttpClient client = new OkHttpClient();
        // 构建请求体
        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        String jsonBody = "{\"name\":\"张三\",\"email\":\"zhangsan@example.com\"}";
        RequestBody body = RequestBody.create(jsonBody, JSON);
        // 构建请求
        Request request = new Request.Builder()
                .url(url)
                .post(body) // .post() 用于发送请求体
                .build();
        // 发送请求并获取响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }
            // 获取响应体字符串
            String responseData = response.body().string();
            System.out.println("Server Response: " + responseData);
            // 在这里处理返回的数据
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}).start();

发送异步请求 (推荐方式): 异步请求不会阻塞调用线程,是处理网络请求的最佳实践。

OkHttpClient client = new OkHttpClient();
String url = "https://your-api-domain.com/api/users";
MediaType JSON = MediaType.get("application/json; charset=utf-8");
String jsonBody = "{\"name\":\"张三\",\"email\":\"zhangsan@example.com\"}";
RequestBody body = RequestBody.create(jsonBody, JSON);
Request request = new Request.Builder()
        .url(url)
        .post(body)
        .build();
// 异步发送请求
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 请求失败,例如网络错误、超时等
        // 注意:此回调在子线程中执行
        e.printStackTrace();
        // 可以通过 Handler 切换到主线程更新 UI
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        // 请求成功,收到服务器响应
        // 注意:此回调也在子线程中执行
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }
        String responseData = response.body().string();
        System.out.println("Server Response: " + responseData);
        // 如果需要更新 UI,必须切换到主线程
        // 使用 Handler, runOnUiThread, 或 LiveData
        new Handler(Looper.getMainLooper()).post(() -> {
            // 在这里更新 UI
            // textView.setText(responseData);
        });
    }
});

使用 Retrofit (高级 REST API 客户端)

Retrofit 是一个类型安全的 HTTP 客户端,它将 REST API 的接口转换为 Java 接口,它内部就是基于 OkHttp 实现的,当你需要与复杂的 API 交互时,Retrofit 是不二之选。

优点

  • 类型安全:通过接口定义 API,编译时检查错误。
  • 代码简洁:将 URL、请求方法、参数等通过注解清晰地定义在接口中。
  • 易于维护:接口和实现分离,结构清晰。
  • 自动转换:可以自动将 JSON 响应体转换成 Java 对象(需要配合 Gson/Moshi/Jackson 等库)。

第一步:添加依赖app/build.gradle 中添加:

// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// JSON 转换器 (这里以 Gson 为例)
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// OkHttp (Retrofit 依赖它,但最好显式声明)
implementation("com.squareup.okhttp3:okhttp:4.12.0")

第二步:定义 API 接口 创建一个 Java 接口,用注解来描述 API。

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface ApiService {
    // @POST 定义了这是一个 POST 请求
    // "api/users" 是相对路径,会与 Retrofit 实例的 baseUrl 拼接
    // @Body 将方法参数自动作为请求体发送
    @POST("api/users")
    Call<ApiResponse> createUser(@Body User user);
}
// 定义请求体对应的 Java 对象
class User {
    private String name;
    private String email;
    // 必须有无参构造函数,并且有 getter/setter 或使用 @SerializedName
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    // Getter and Setter...
}
// 定义服务器响应对应的 Java 对象
class ApiResponse {
    private String status;
    private String message;
    private String user_id;
    // Getter and Setter...
}

第三步:创建 Retrofit 实例并发送请求

public class ApiClient {
    private static Retrofit retrofit = null;
    public static Retrofit getClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl("https://your-api-domain.com/") // 必须以 / 
                    .addConverterFactory(GsonConverterFactory.create()) // 添加 JSON 转换器
                    .build();
        }
        return retrofit;
    }
}
// 在 Activity 或其他地方调用
ApiService apiService = ApiClient.getClient().create(ApiService.class);
// 创建请求体对象
User user = new User("张三", "zhangsan@example.com");
// 发起异步请求
Call<ApiResponse> call = apiService.createUser(user);
call.enqueue(new Callback<ApiResponse>() {
    @Override
    public void onFailure(Call<ApiResponse> call, Throwable t) {
        // 请求失败
        t.printStackTrace();
    }
    @Override
    public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) {
        // 请求成功
        if (response.isSuccessful()) {
            ApiResponse apiResponse = response.body();
            if (apiResponse != null) {
                System.out.println("Status: " + apiResponse.getStatus());
                System.out.println("Message: " + apiResponse.getMessage());
                // 更新 UI...
            }
        } else {
            // 处理错误响应,response.code() == 400
            System.out.println("Error: " + response.code());
        }
    }
});

数据格式选择:JSON vs. Form-Data

  • JSON (application/json):

    • 优点:结构化,易于人阅读和机器解析,是现代 Web API 的标准,适合发送复杂、嵌套的数据。
    • 缺点:相比表单数据,体积稍大。
    • 用法:如上所示,构建一个 JSON 字符串或对象,设置 Content-Typeapplication/json
  • Form-Data (application/x-www-form-urlencoded 或 multipart/form-data):

    • 优点:传统的表单提交格式,简单直接。multipart/form-data 特别适合用来上传文件。

    • 缺点:不适合表示复杂的数据结构。

    • 用法:

      • application/x-www-form-urlencoded (键值对):

        // OkHttp 示例
        FormBody formBody = new FormBody.Builder()
                .add("name", "张三")
                .add("email", "zhangsan@example.com")
                .build();
        Request request = new Request.Builder()
                .url("https://your-api-domain.com/api/users")
                .post(formBody)
                .build();
      • multipart/form-data (常用于文件上传):

        // OkHttp 示例
        MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
        File file = new File("/path/to/image.png");
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("username", "zhangsan")
                .addFormDataPart("profile_picture", "image.png",
                        RequestBody.create(file, MEDIA_TYPE_PNG))
                .build();
        Request request = new Request.Builder()
                .url("https://your-api-domain.com/api/upload")
                .post(requestBody)
                .build();

关键注意事项

  1. 网络权限: 别忘了在 AndroidManifest.xml 中添加 <uses-permission android:name="android.permission.INTERNET" />
  2. 子线程: 所有网络操作都不能在主线程(UI线程)中执行,否则会抛出 NetworkOnMainThreadException 异常,必须使用异步请求(如 okhttpenqueueretrofitenqueue)或在子线程中执行同步请求。
  3. 数据安全: 生产环境中,务必使用 HTTPS 协议,以防止数据在传输过程中被窃听或篡改。
  4. 错误处理: 妥善处理网络错误(如无连接、超时)和服务器返回的错误状态码(如 404, 500)。
  5. 超时设置: 对于 OkHttp,可以设置连接、读取和写入的超时时间,防止请求长时间卡住。
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

完整示例:使用 OkHttp 发送 JSON 数据

假设我们要将一个用户信息发送到服务器。

添加依赖和权限 (如上所述)

在 Activity 中调用

public class MainActivity extends AppCompatActivity {
    private static final String API_URL = "https://your-api-domain.com/api/users";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button sendButton = findViewById(R.id.send_button);
        sendButton.setOnClickListener(v -> sendData());
    }
    private void sendData() {
        // 1. 构建要发送的 JSON 数据
        String jsonBody = "{\"name\":\"李四\",\"email\":\"lisi@example.com\"}";
        // 2. 创建 OkHttpClient 实例
        OkHttpClient client = new OkHttpClient();
        // 3. 创建 RequestBody
        MediaType mediaType = MediaType.get("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(jsonBody, mediaType);
        // 4. 创建 Request
        Request request = new Request.Builder()
                .url(API_URL)
                .post(body)
                .build();
        // 5. 发送异步请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                // 请求失败,在子线程中执行
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "发送失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                });
            }
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                // 请求成功,在子线程中执行
                if (response.isSuccessful()) {
                    String responseData = response.body().string();
                    runOnUiThread(() -> {
                        Toast.makeText(MainActivity.this, "发送成功! 响应: " + responseData, Toast.LENGTH_LONG).show();
                    });
                } else {
                    runOnUiThread(() -> {
                        Toast.makeText(MainActivity.this, "服务器错误: " + response.code(), Toast.LENGTH_SHORT).show();
                    });
                }
            }
        });
    }
}

进阶话题

  • 文件上传: 如上所述,使用 MultipartBody 可以轻松实现文件上传。
  • 断点续传: 需要客户端记录已上传的字节数,并在请求头中 (Range) 告诉服务器从哪里开始继续上传,OkHttp 和 Retrofit 本身不直接支持,需要自定义实现。
  • 进度监听: OkHttp 本身不直接提供上传/下载进度回调,可以通过继承 RequestBodyResponseBody 并重写相关方法来实现自定义的进度监听,很多第三方库(如 OkHttp-Progress)已经封装好了这个功能。
方式 优点 缺点 适用场景
HttpURLConnection 无需依赖 代码繁琐,功能弱 简单 demo 或对依赖有严格限制的项目
OkHttp 性能好,API简洁,功能强大 相比原生 API 仍是第三方库 绝大多数 Android 网络请求场景,是事实上的标准
Retrofit 类型安全,代码清晰,易于维护 需要学习成本,是 OkHttp 的上层封装 与 RESTful API 交互复杂、需要将 JSON 自动映射为 Java 对象的项目

对于新项目,强烈推荐直接从 Retrofit 开始,因为它能让你写出更健壮、更易于维护的代码,如果只是需要一个简单的网络工具,OkHttp 也非常优秀,尽量避免直接使用 HttpURLConnection 除非有特殊原因。

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