13、网络请求基础:HttpURLConnection与OkHttp的使用,GET/POST请求,JSON数据解析(Gson/FastJson)

网络请求,说白了就是你的App跟服务器“打电话”。我刚开始做Android开发那会儿,网络这块真是踩了不少坑。今天咱们就把HttpURLConnection和OkHttp这两个最常用的网络库讲透,顺便把JSON解析也一并拿下。

13.1 网络请求的核心流程

不管用哪个库,网络请求的套路其实都差不多。我习惯把它分成三步:

  1. 建立连接——告诉服务器我要找谁
  2. 发送数据——把请求发出去(GET或POST)
  3. 解析响应——把服务器返回的数据变成我们能用的对象

你想想看,这跟你去餐厅点餐是一个道理:先找到服务员(建立连接),然后点菜(发送请求),最后菜端上来你吃掉(解析数据)。

网络请求核心流程 ① 建立连接 URL / 端口 / 协议 ② 发送请求 GET / POST + 参数 ③ 解析响应 JSON → Java对象 ⚠ 注意:所有网络操作必须在子线程执行,不能在主线程搞! Android 4.0+ 强制要求,否则直接抛 NetworkOnMainThreadException

13.2 HttpURLConnection——Android自带的“老伙计”

HttpURLConnection是JDK自带的网络类,Android里也能直接用。说实话,它用起来有点啰嗦,但胜在不需要引入任何第三方库。我在早期维护一个老项目时,用的就是它。

13.2.1 GET请求示例

// 创建一个URL对象
URL url = new URL("https://api.example.com/users?page=1");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// 设置请求方式
connection.setRequestMethod("GET");
// 设置超时时间(单位:毫秒)
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

// 设置请求头(模拟浏览器)
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Accept", "application/json");

// 获取响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
    // 读取响应数据
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(connection.getInputStream()));
    StringBuilder response = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        response.append(line);
    }
    reader.close();
    System.out.println("响应数据:" + response.toString());
} else {
    System.out.println("请求失败,响应码:" + responseCode);
}

// 记得关闭连接
connection.disconnect();
💡 我的小习惯:每次用完HttpURLConnection,一定要调disconnect()。我曾经因为忘记关闭连接,导致线上App出现内存泄漏,排查了好久才发现是连接没释放。

13.2.2 POST请求示例

URL url = new URL("https://api.example.com/users");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("POST");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

// POST请求必须开启输出流
connection.setDoOutput(true);
connection.setDoInput(true);

// 设置请求头
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");

// 构建请求体(JSON格式)
String jsonBody = "{\"name\":\"张三\",\"age\":25}";

// 写入请求体
OutputStream os = connection.getOutputStream();
os.write(jsonBody.getBytes("UTF-8"));
os.flush();
os.close();

int responseCode = connection.getResponseCode();
// ... 读取响应的代码跟GET一样,这里省略

connection.disconnect();
⚠ 避坑指南:我曾经在POST请求里忘了设置setDoOutput(true),结果服务器一直收不到数据。这个属性默认是false,必须手动开启。另外,Content-Type一定要写对,不然服务器解析不了你的JSON。

13.3 OkHttp——更优雅的网络请求方案

说实话,现在大部分项目都在用OkHttp。它比HttpURLConnection好用太多了,链式调用、连接池复用、自动重试……这些特性让开发效率提升不少。

要使用OkHttp,先在build.gradle里加依赖:

implementation 'com.squareup.okhttp3:okhttp:4.12.0'

13.3.1 GET请求示例

// 创建OkHttpClient实例(建议全局单例)
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .build();

// 构建请求
Request request = new Request.Builder()
        .url("https://api.example.com/users?page=1")
        .addHeader("User-Agent", "Mozilla/5.0")
        .addHeader("Accept", "application/json")
        .get()
        .build();

// 同步执行(需要在子线程)
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        String responseBody = response.body().string();
        System.out.println("响应数据:" + responseBody);
    } else {
        System.out.println("请求失败:" + response.code());
    }
} catch (IOException e) {
    e.printStackTrace();
}

13.3.2 POST请求示例

// 构建JSON请求体
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String jsonBody = "{\"name\":\"李四\",\"age\":30}";
RequestBody body = RequestBody.create(jsonBody, JSON);

// 构建请求
Request request = new Request.Builder()
        .url("https://api.example.com/users")
        .post(body)
        .addHeader("Accept", "application/json")
        .build();

// 执行请求
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        String responseBody = response.body().string();
        System.out.println("响应数据:" + responseBody);
    }
} catch (IOException e) {
    e.printStackTrace();
}
🔑 关键区别:OkHttp的response.body().string()只能调用一次!我第一次用的时候没注意,调了两次,第二次返回空字符串。原因是OkHttp的响应体是流式的,读完就没了。如果需要多次使用,先存到变量里。

13.4 JSON数据解析——Gson vs FastJson

服务器返回的数据通常是JSON格式,我们需要把它转成Java对象。目前最主流的两个库是Gson(Google出品)和FastJson(阿里巴巴出品)。

先加依赖:

// Gson
implementation 'com.google.code.gson:gson:2.10.1'

// FastJson
implementation 'com.alibaba:fastjson:2.0.40'

13.4.1 定义数据模型

假设服务器返回的JSON长这样:

{
  "code": 200,
  "message": "成功",
  "data": {
    "id": 1001,
    "name": "王五",
    "age": 28,
    "email": "wangwu@example.com"
  }
}

对应的Java类:

// 外层响应类
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
    
    // getter/setter 省略
}

// 用户信息类
public class User {
    private int id;
    private String name;
    private int age;
    private String email;
    
    // getter/setter 省略
}

13.4.2 使用Gson解析

// 拿到服务器返回的JSON字符串
String jsonString = "{\"code\":200,\"message\":\"成功\",\"data\":{\"id\":1001,\"name\":\"王五\",\"age\":28,\"email\":\"wangwu@example.com\"}}";

// 创建Gson实例
Gson gson = new Gson();

// 解析为ApiResponse对象
// 注意:这里要用TypeToken来获取泛型类型
Type type = new TypeToken<ApiResponse<User>>() {}.getType();
ApiResponse<User> response = gson.fromJson(jsonString, type);

// 获取数据
if (response.getCode() == 200) {
    User user = response.getData();
    System.out.println("用户名:" + user.getName());
    System.out.println("邮箱:" + user.getEmail());
}

13.4.3 使用FastJson解析

// FastJson解析,代码更简洁
String jsonString = "{\"code\":200,\"message\":\"成功\",\"data\":{\"id\":1001,\"name\":\"王五\",\"age\":28,\"email\":\"wangwu@example.com\"}}";

// 直接解析,不需要TypeToken
ApiResponse<User> response = JSON.parseObject(jsonString, 
    new TypeReference<ApiResponse<User>>() {});

if (response.getCode() == 200) {
    User user = response.getData();
    System.out.println("用户名:" + user.getName());
}
💡 我的选择:我个人习惯用Gson,因为它是Google官方维护的,跟Android生态更契合。但FastJson在解析速度上确实有优势,尤其是在数据量大的场景。你想想看,如果接口返回的数据有上万条,FastJson能快个几十毫秒。不过日常开发中,这点差距基本感觉不到。

13.5 实战:完整的网络请求+解析流程

咱们把上面学的串起来,写一个完整的例子:

public class NetworkHelper {
    
    private static OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .build();
    
    private static Gson gson = new Gson();
    
    /**
     * 发送GET请求并解析为指定类型
     */
    public static <T> ApiResponse<T> doGet(String url, Type type) {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        
        try {
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                String jsonString = response.body().string();
                return gson.fromJson(jsonString, type);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 发送POST请求并解析为指定类型
     */
    public static <T> ApiResponse<T> doPost(String url, Object requestBody, Type type) {
        String jsonBody = gson.toJson(requestBody);
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(jsonBody, JSON);
        
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        
        try {
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                String jsonString = response.body().string();
                return gson.fromJson(jsonString, type);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
⚠ 重要提醒:上面的代码是同步请求,必须在子线程执行。如果在主线程调用,会直接崩溃。实际项目中建议配合线程池或协程使用。另外,网络请求的异常处理一定要做全,不要只catch一个IOException,网络超时、DNS解析失败、服务器500错误……各种情况都要考虑到。

13.6 本章小结

网络请求这块,说白了就是三板斧:建连接、发请求、解析数据。HttpURLConnection是基本功,OkHttp是进阶利器。我个人建议新项目直接用OkHttp,省心省力。JSON解析方面,Gson和FastJson二选一就行,不用纠结。

嗯,这里要注意一点:网络请求的代码一定要做好封装,不要到处new OkHttpClient。我见过一个项目里每个Activity都自己创建HttpURLConnection,代码冗余不说,连接也没复用,性能很差。封装成一个工具类,全局维护一个OkHttpClient实例,这才是正确的姿势。


公众号:蓝海资料掘金营,微信deep3321