14. 网络编程基础:从HttpURLConnection到Retrofit+RxJava

网络请求,说白了就是你的App跟服务器“打电话”。我刚开始学Android那会儿,最头疼的就是这个——数据怎么发出去?怎么收回来?JSON又是什么鬼?别急,今天咱们把这几个问题一次性捋清楚。

本章核心脉络:原生HttpURLConnection → OkHttp封装 → Retrofit+RxJava响应式 → JSON解析 → 最终封装成一个通用网络层。

网络编程知识体系 Android 网络请求 HttpURLConnection OkHttp Retrofit+RxJava JSON解析(Gson/FastJson) 网络请求封装(通用层) 从底层到上层,逐步抽象,最终实现一行代码发起网络请求

14.1 HttpURLConnection:最原生的方式

Android刚起步那几年,HttpURLConnection是官方推荐的选择。它轻量、直接,但用起来确实有点“原始”。

我个人习惯在简单GET请求时用它,比如拉个配置、取个版本号。但你要是做复杂请求,嗯,代码量会有点感人。

// 一个简单的GET请求
URL url = new URL("https://api.example.com/users");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);

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();
    // 拿到response.toString() 就是服务器返回的数据
}
connection.disconnect();

注意:网络请求不能在主线程执行,否则会抛出NetworkOnMainThreadException。记得放到子线程或使用AsyncTask。

我曾经在一个老项目里看到有人直接在Activity里new Thread做网络请求,然后通过Handler更新UI。虽然能跑,但代码维护起来真是噩梦。所以后来我基本只用OkHttp或Retrofit了。

14.2 OkHttp:更优雅的封装

OkHttp是Square公司出品的网络库,现在已经是Android网络请求的事实标准了。它解决了HttpURLConnection的很多痛点——比如连接池复用、自动重定向、拦截器机制等等。

你想想看,如果你用HttpURLConnection,每次请求都要手动创建连接、设置参数、处理异常。而OkHttp把这些都封装好了。

// 创建OkHttpClient,建议单例
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(10, TimeUnit.SECONDS)
    .addInterceptor(new HttpLoggingInterceptor() // 日志拦截器
        .setLevel(HttpLoggingInterceptor.Level.BODY))
    .build();

// 构建请求
Request request = new Request.Builder()
    .url("https://api.example.com/users")
    .get()
    .addHeader("Authorization", "Bearer token123")
    .build();

// 同步请求(会阻塞线程)
Response response = client.newCall(request).execute();
String result = response.body().string();

// 异步请求(推荐)
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 网络失败处理
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            String data = response.body().string();
            // 切回主线程更新UI
        }
    }
});

我的经验:OkHttp的拦截器机制非常强大。我曾在项目中用拦截器统一添加Token、打印请求日志、处理重试逻辑。你只需要实现一个Interceptor接口,然后add进去就行。

14.3 Retrofit + RxJava:响应式网络请求

Retrofit是什么?说白了,它就是OkHttp的“升级版”——帮你把网络请求定义成Java接口,然后自动生成实现代码。配合RxJava,可以实现链式调用、线程切换、错误处理一体化。

我第一次用Retrofit的时候,感觉就像发现了新大陆。以前写几十行代码的请求,现在几行就搞定了。

// 1. 定义接口
public interface ApiService {
    @GET("users")
    Observable<List<User>> getUsers();

    @POST("user")
    Observable<User> createUser(@Body User user);
}

// 2. 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create()) // JSON自动解析
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // RxJava支持
    .client(new OkHttpClient()) // 底层还是OkHttp
    .build();

// 3. 发起请求
ApiService service = retrofit.create(ApiService.class);
service.getUsers()
    .subscribeOn(Schedulers.io()) // 网络请求在IO线程
    .observeOn(AndroidSchedulers.mainThread()) // 结果回调在主线程
    .subscribe(new Observer<List<User>>() {
        @Override
        public void onSubscribe(Disposable d) {
            // 可以用于取消请求
        }

        @Override
        public void onNext(List<User> users) {
            // 拿到数据,更新UI
        }

        @Override
        public void onError(Throwable e) {
            // 统一错误处理
        }

        @Override
        public void onComplete() {
            // 请求完成
        }
    });

为什么用RxJava? 因为它解决了Android开发中两个核心问题:异步线程切换和回调嵌套。你想想看,如果没有RxJava,你要先new Thread,然后在回调里runOnUiThread,代码会变得非常臃肿。

14.4 JSON解析:Gson vs FastJson

服务器返回的数据通常是JSON格式,我们需要把它解析成Java对象。Gson和FastJson是两种主流选择。

特性 Gson FastJson
开发者 Google 阿里巴巴
解析速度 中等 较快
内存占用 较低 中等
易用性 注解驱动,规范 API简洁,但坑较多
稳定性 非常稳定 部分版本有bug

我个人习惯用Gson,因为它是Google官方维护的,和Android生态配合得很好。FastJson虽然快,但我曾经遇到过循环引用导致栈溢出的问题,排查了很久。

// Gson解析示例
String json = "{ \"name\": \"张三\", \"age\": 25 }";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);

// 如果字段名和JSON不一致,用@SerializedName注解
public class User {
    @SerializedName("user_name")
    private String name;
    private int age;
    // getter/setter...
}

// FastJson解析示例
User user = JSON.parseObject(json, User.class);

避坑指南:我曾经在解析嵌套JSON时,忘记给内部类加static修饰符,结果Gson一直报错。记住:内部类一定要用static,否则Gson无法实例化。

14.5 网络请求封装:打造通用网络层

在实际项目中,我们不会每次都手动创建Retrofit、OkHttpClient。而是封装成一个工具类,统一管理。

嗯,这里要注意:封装不是越复杂越好。我见过有人封装了七八层,结果改个超时时间都要翻半天代码。我的原则是——够用就好。

// 一个简洁的网络请求封装
public class NetworkManager {
    private static volatile NetworkManager instance;
    private Retrofit retrofit;
    private ApiService apiService;

    private NetworkManager() {
        OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .addInterceptor(new TokenInterceptor()) // 自动添加Token
            .build();

        retrofit = new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build();

        apiService = retrofit.create(ApiService.class);
    }

    public static NetworkManager getInstance() {
        if (instance == null) {
            synchronized (NetworkManager.class) {
                if (instance == null) {
                    instance = new NetworkManager();
                }
            }
        }
        return instance;
    }

    public ApiService getApiService() {
        return apiService;
    }

    // 统一错误处理
    public static <T> Observer<T> getDefaultObserver(final NetworkCallback<T> callback) {
        return new Observer<T>() {
            @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onNext(T t) {
                callback.onSuccess(t);
            }

            @Override
            public void onError(Throwable e) {
                // 统一解析错误类型
                if (e instanceof SocketTimeoutException) {
                    callback.onError("网络超时,请重试");
                } else if (e instanceof UnknownHostException) {
                    callback.onError("网络不可用");
                } else {
                    callback.onError("请求失败:" + e.getMessage());
                }
            }

            @Override
            public void onComplete() {}
        };
    }

    public interface NetworkCallback<T> {
        void onSuccess(T data);
        void onError(String errorMsg);
    }
}

// 使用示例
NetworkManager.getInstance().getApiService()
    .getUsers()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(NetworkManager.getDefaultObserver(new NetworkManager.NetworkCallback<List<User>>() {
        @Override
        public void onSuccess(List<User> users) {
            // 更新UI
        }

        @Override
        public void onError(String errorMsg) {
            // 显示错误提示
        }
    }));

封装的核心思想:把变化的部分(如baseUrl、超时时间)放到配置文件中,把不变的部分(如线程切换、错误处理)固化在封装层里。这样后期维护起来特别轻松。

好了,网络编程这块儿就聊到这儿。从HttpURLConnection到Retrofit+RxJava,其实就是一个不断“偷懒”的过程——把重复的工作交给框架,把精力放在业务逻辑上。你学会了吗?


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