14. 网络编程基础:从HttpURLConnection到Retrofit+RxJava
网络请求,说白了就是你的App跟服务器“打电话”。我刚开始学Android那会儿,最头疼的就是这个——数据怎么发出去?怎么收回来?JSON又是什么鬼?别急,今天咱们把这几个问题一次性捋清楚。
本章核心脉络:原生HttpURLConnection → OkHttp封装 → Retrofit+RxJava响应式 → JSON解析 → 最终封装成一个通用网络层。
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 |
|---|---|---|
| 开发者 | 阿里巴巴 | |
| 解析速度 | 中等 | 较快 |
| 内存占用 | 较低 | 中等 |
| 易用性 | 注解驱动,规范 | 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