Retrofit+RxJava:打造声明式REST客户端的艺术 —— 像点咖啡一样调用API

发布于:2025-07-15 ⋅ 阅读:(17) ⋅ 点赞:(0)

场景漫游:想象你在星巴克告诉咖啡师"一杯中杯拿铁,加燕麦奶,75度",后端服务调用API也该如此优雅!今天我们将用Retrofit+RxJava这对黄金组合,实现比咖啡订单更丝滑的API调用体验。


一、告别Callback地狱:声明式编程的魅力

1.1 传统网络请求的痛点

// 传统回调地狱案例
apiService.getUser(userId, new Callback<User>() {
    @Override
    public void onResponse(Call<User> call, Response<User> response) {
        if (response.isSuccessful()) {
            apiService.getOrders(response.body().getId(), new Callback<List<Order>>() {
                @Override
                public void onResponse(Call<List<Order>> call, Response<List<Order>> response) {
                    // 处理订单数据...
                }
                // 省略错误处理...
            });
        }
    }
    // 省略错误处理...
});

灾难现场
🌀 嵌套金字塔结构
🕳️ 错误处理分散
⏳ 线程切换复杂

1.2 Retrofit的声明式革命

public interface GitHubService {
    @GET("users/{user}/repos")
    Observable<List<Repo>> listRepos(@Path("user") String user);
}

魔法解析
✅ 接口即文档
✅ 参数注解自动拼接URL
✅ 返回值类型自由定义(支持RxJava类型)


二、Retrofit核心配置工厂

2.1 构建Retrofit实例

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create()) // JSON转换
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // RxJava支持
    .client(new OkHttpClient.Builder()
        .addInterceptor(new HttpLoggingInterceptor()) // 请求日志
        .connectTimeout(10, TimeUnit.SECONDS)
        .build())
    .build();

2.2 常用注解速查表

注解 作用示例 对应HTTP元素
@GET @GET(“users/{id}”) GET请求
@Path @Path(“id”) Long userId URL路径参数
@Query @Query(“sort”) String sort URL查询参数
@Body @Body LoginRequest request 请求体
@Header @Header(“Authorization”) 请求头

三、RxJava响应式魔法

3.1 链式调用优雅变身

// 获取用户资料后获取最新动态
apiService.getUserProfile(userId)
    .flatMap(user -> apiService.getLatestActivities(user.getId()))
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        activities -> showActivities(activities),
        error -> showError(error)
    );

操作符流程图

(GetUser) → (FlatMap) → (GetActivities) → (UI展示)

3.2 超能力操作符盘点

// 并行请求用户信息和订单列表
Observable.zip(
    apiService.getUserInfo(userId),
    apiService.getUserOrders(userId),
    (userInfo, orders) -> new UserDashboard(userInfo, orders)
).subscribe(dashboard -> updateUI(dashboard));

// 带超时和重试的请求
apiService.fetchData()
    .timeout(5, TimeUnit.SECONDS)
    .retryWhen(errors -> errors.flatMap(error -> {
        if (error instanceof SocketTimeoutException) {
            return Observable.timer(1, TimeUnit.SECONDS);
        }
        return Observable.error(error);
    }))
    .subscribe(...);

四、实战演练:构建天气查询客户端

4.1 定义天气接口

public interface WeatherService {
    @GET("weather")
    Observable<WeatherData> getWeatherByCity(
        @Query("q") String city,
        @Query("appid") String apiKey
    );
}

4.2 ViewModel中集成

public class WeatherViewModel extends ViewModel {
    private final CompositeDisposable disposables = new CompositeDisposable();

    public void loadWeather(String city) {
        disposables.add(weatherService.getWeatherByCity(city, API_KEY)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSubscribe(d -> loadingState.setValue(true))
            .doFinally(() -> loadingState.setValue(false))
            .subscribe(
                data -> weatherData.postValue(data),
                error -> showError(error)
            ));
    }

    @Override
    protected void onCleared() {
        disposables.clear();
    }
}

4.3 动态更新UI

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    onRefresh="刷新天气数据">

    <TextView
        android:id="@+id/tvTemperature"
        android:text="@{viewModel.weatherData.temp}℃" />
    
    <ImageView
        android:src="@{viewModel.weatherData.getWeatherIcon()}" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

五、高级技巧:错误处理艺术

5.1 全局错误拦截器

public class GlobalErrorInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        if (!response.isSuccessful()) {
            throw new HttpException(response.code(), response.message());
        }
        return response;
    }
}

// 注入OkHttpClient
new OkHttpClient.Builder()
    .addInterceptor(new GlobalErrorInterceptor())

5.2 响应统一包装

public class ApiResponse<T> {
    private T data;
    private Throwable error;
    
    public Observable<ApiResponse<T>> wrap(Observable<T> origin) {
        return origin
            .map(data -> new ApiResponse<>(data))
            .onErrorReturn(error -> new ApiResponse<>(error));
    }
}

// 使用示例
apiService.fetchData()
    .compose(new ApiResponseWrapper())
    .subscribe(response -> {
        if (response.hasError()) {
            // 处理错误
        } else {
            // 处理数据
        }
    });

六、性能优化实战指南

6.1 请求缓存策略

// 缓存控制拦截器
public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (NetworkUtils.isNetworkAvailable()) {
            request = request.newBuilder()
                .header("Cache-Control", "public, max-age=60")
                .build();
        } else {
            request = request.newBuilder()
                .header("Cache-Control", 
                    "public, only-if-cached, max-stale=604800")
                .build();
        }
        return chain.proceed(request);
    }
}

6.2 合并重复请求

// 使用RxJava的cache()操作符
Observable<List<Product>> productsObservable = apiService.getProducts()
    .cache()
    .subscribeOn(Schedulers.io());

// 多个订阅者共享结果
productsObservable.subscribe(products -> updateList(products));
productsObservable.subscribe(products -> updateChart(products));

七、你该这样思考:架构师的视角

当你熟练使用Retrofit+RxJava后,可以尝试:

  1. 封装统一网络层模块
  2. 实现自动Token刷新机制
  3. 与Kotlin协程结合使用
  4. 编写API Mock测试模块
  5. 实施流量监控与报警

站在咖啡店的落地窗前,看着自己构建的声明式API客户端——它就像一杯精心调制的咖啡,每个操作符都是独特的调味剂,而Retrofit则是那个精准控制水温的咖啡机。现在,是时候用这些工具构建出属于你自己的美味代码了! ☕🚀


网站公告

今日签到

点亮在社区的每一天
去签到