GraphQL 集成:什么是 GraphQL、Apollo Kotlin 客户端、与 Retrofit 对比

说到网络层,大家第一反应肯定是 Retrofit + OkHttp。这套组合拳确实能打,但说实话,它并不是万能的。我在几年前接手过一个项目,后端接口动不动就返回几十个字段,而前端只需要其中三四个。每次看到那坨 JSON,我都觉得带宽在哭泣。后来我们引入了 GraphQL,情况才好转。

这一章,我们就来聊聊 GraphQL 在 Android 端的落地。我会从概念讲起,然后带你上手 Apollo Kotlin 客户端,最后再跟 Retrofit 做个对比。嗯,保证都是实战干货。

什么是 GraphQL?

GraphQL 是一种 API 查询语言,由 Facebook 在 2012 年内部开发,2015 年开源。它的核心思想是:让客户端精确指定需要的数据

你想想看,传统的 REST API 是服务端定义好返回结构,客户端只能全盘接收。而 GraphQL 反过来——客户端写查询语句,服务端按需返回。说白了,就是「你要什么,我给你什么」。

核心特点:

  • 精确获取:只返回客户端请求的字段,不多不少
  • 单一端点:所有操作都通过一个 /graphql 端点完成
  • 强类型系统:Schema 定义了所有可查询的类型和关系
  • 实时订阅:原生支持 WebSocket 订阅,实现实时推送

举个例子。假设我们要获取用户信息和他的订单列表。用 REST 的话,你可能需要调两个接口:/users/1/users/1/orders。用 GraphQL,一个查询搞定:

query {
  user(id: "1") {
    name
    email
    orders {
      id
      total
      items {
        name
        price
      }
    }
  }
}

返回的数据结构跟查询语句一模一样。这就是 GraphQL 最迷人的地方——查询即文档

Apollo Kotlin 客户端

在 Android 端集成 GraphQL,目前最成熟的选择就是 Apollo Kotlin 客户端。它由 Apollo GraphQL 公司维护,支持 Kotlin 协程和 Flow,跟我们的技术栈完美契合。

快速上手

第一步,在 build.gradle.kts 中添加依赖:

// 项目级 build.gradle.kts
plugins {
    id("com.apollographql.apollo3") version "3.8.2"
}

// 模块级 build.gradle.kts
dependencies {
    implementation("com.apollographql.apollo3:apollo-runtime:3.8.2")
    implementation("com.apollographql.apollo3:apollo-coroutines-support:3.8.2")
}

apollo {
    service("default") {
        packageName.set("com.example.graphql")
    }
}

第二步,定义 GraphQL 查询文件。在 src/main/graphql 目录下创建 GetUser.graphql

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
    avatar
  }
}

第三步,Apollo 会根据这个文件自动生成 Kotlin 代码。然后你就可以在 ViewModel 中愉快地调用了:

class UserViewModel : ViewModel() {
    private val apolloClient = ApolloClient.Builder()
        .serverUrl("https://api.example.com/graphql")
        .build()

    fun getUser(id: String): Flow<UiState<User>> = flow {
        emit(UiState.Loading)
        val response = apolloClient
            .query(GetUserQuery(id = id))
            .toFlow()
            .collect { result ->
                val user = result.data?.user?.let {
                    User(it.id, it.name, it.email, it.avatar)
                }
                emit(UiState.Success(user))
            }
    }.catch { e ->
        emit(UiState.Error(e.message ?: "未知错误"))
    }
}

我个人习惯把 ApolloClient 做成单例,用依赖注入管理。这样测试的时候也方便 mock。

小技巧:Apollo 支持自动缓存。你可以配置 NormalizedCache,这样相同的查询不会重复请求网络。我在项目中用这个功能减少了 30% 的网络开销。

与协程和 Flow 的整合

Apollo Kotlin 对协程的支持非常原生。它提供了 toFlow() 扩展函数,可以把查询结果转成 Flow。配合 catchretry 操作符,错误处理变得很优雅。

我曾经遇到过一个坑:Apollo 默认的 toFlow() 在查询失败时会直接抛异常,不会发射错误状态。后来我发现需要手动处理 ApolloResponse.exception

apolloClient.query(query).toFlow().collect { response ->
    if (response.hasErrors()) {
        // 处理 GraphQL 层面的错误
        emit(UiState.Error(response.errors?.first()?.message ?: "GraphQL error"))
    } else if (response.exception != null) {
        // 处理网络层面的错误
        emit(UiState.Error(response.exception.message ?: "Network error"))
    } else {
        // 正常数据
        emit(UiState.Success(response.data))
    }
}

与 Retrofit 对比

很多同学会问:有了 Retrofit,为什么还要用 GraphQL?它们不是替代关系,而是互补关系。我整理了一个对比表格,方便你理解:

维度 Retrofit + REST Apollo + GraphQL
数据获取 服务端定义返回结构,客户端全盘接收 客户端精确指定字段,按需返回
端点数量 多个端点(/users, /orders, /products...) 单一端点(/graphql)
类型安全 需要手动定义 DTO 和解析器 自动生成 Kotlin 代码,编译期类型检查
缓存策略 通常配合 OkHttp 缓存或 Room 内置 NormalizedCache,自动去重
实时推送 需要额外集成 WebSocket 库 原生支持 Subscription
学习曲线 低,大部分开发者熟悉 中等,需要理解 Schema 和查询语法
适用场景 简单 CRUD、第三方 API 集成 复杂数据关系、多端复用、实时场景

说白了,如果你的项目只是简单的列表展示、表单提交,Retrofit 完全够用。但如果你遇到以下情况,GraphQL 会更合适:

  • 数据关系复杂:比如社交应用,用户、帖子、评论、点赞互相嵌套
  • 多端复用:同一个 API 要服务 Android、iOS、Web,每个端需要的字段不同
  • 带宽敏感:移动端网络环境差,能省一点是一点
  • 实时需求:聊天、通知、实时数据看板

注意:GraphQL 不是银弹。它的查询语句可能很复杂,服务端实现也有一定门槛。如果你的后端团队不熟悉 GraphQL,强行引入反而会增加沟通成本。我建议先在小模块试点,验证效果后再推广。

知识体系结构图

下面这张图展示了 GraphQL 在 Android 网络层中的位置,以及它与 Retrofit 的关系:

Android 网络层:GraphQL vs REST Android 客户端 Apollo Kotlin GraphQL 客户端 Retrofit REST 客户端 后端服务 单一端点 /graphql 多个端点 /users, /orders... 精确查询 · 自动代码生成 · 实时订阅 简单直接 · 生态成熟 · 学习成本低

从这张图可以看得很清楚:Apollo Kotlin 和 Retrofit 都位于客户端与后端之间,但它们的通信方式完全不同。Apollo 走的是单一端点、精确查询的路线;Retrofit 走的是多端点、全量返回的路线。选择哪个,取决于你的业务场景。

我的建议

如果你现在正在搭建一个新项目,我的建议是:不要盲目追求新技术。先评估团队的技术栈和后端的支持情况。

  • 如果后端已经提供了 GraphQL 接口,那 Apollo Kotlin 是首选。它的类型安全和自动代码生成能省不少事。
  • 如果后端是传统的 REST 接口,Retrofit 依然是王者。别为了用 GraphQL 而强行改造后端。
  • 如果项目既有 REST 又有 GraphQL,两者可以共存。我在一个项目中同时用了 Retrofit 和 Apollo,互不干扰。

嗯,这一章的内容就到这里。GraphQL 是个好东西,但要用对地方。下一章我们会深入 Apollo 的缓存机制,看看它是如何自动管理数据状态的。


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