You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin JVM应用中结合runBlocking使用Apollo Client?

Kotlin JVM 中用 Apollo Client 实现分页数据合并的正确方式

先看你代码里的几个核心问题,这是导致数据为空的直接原因:

  1. 变量名错误:FooSource的load方法里,用response接收查询结果,但返回LoadResult时却引用了未定义的result.data,应该替换为response.data(还要注意处理response.data可能为null的情况)。
  2. 拼写错误:getData里实例化的是FoodSource,但你的类名是FooSource,这会导致编译错误。
  3. 参数未初始化:getData里的params没有初始化,需要先创建PageParams实例,设置初始的pageSize和空游标。
  4. 分页标记提取错误:你需要从Apollo查询结果的pageInfo中正确提取endCursor和hasNextPage,而不是凭空生成。

修正后的基础实现

先修复错误,让代码能正常运行:

// 定义分页参数类
data class PageParams(
    val pageSize: Int,
    var cursorNext: String? = null
)

// 定义加载结果类
sealed class LoadResult<out T> {
    data class Success<out T>(val data: List<T>, val endCursor: String?, val hasNext: Boolean) : LoadResult<T>()
    data class Error(val exception: Exception) : LoadResult<Nothing>()
}

class FooSource(private val query: String, private val apolloClient: ApolloClient) {

    suspend fun load(params: PageParams): LoadResult<Foo> {
        return try {
            val response = apolloClient.query(
                FooQuery(
                    queryString = query,
                    pageSize = params.pageSize,
                    cursor = params.cursorNext
                )
            ).execute()

            // 处理GraphQL层面的错误
            if (response.hasErrors()) {
                LoadResult.Error(Exception(response.errors?.joinToString { it.message }))
            } else {
                val fooData = response.data?.foos ?: emptyList()
                val pageInfo = response.data?.pageInfo
                val endCursor = pageInfo?.endCursor
                val hasNext = pageInfo?.hasNextPage ?: false
                
                println("Source data: ${fooData.size}")
                LoadResult.Success(fooData, endCursor, hasNext)
            }
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}

// 合并分页数据的函数
fun getData(query: String, apolloClient: ApolloClient, pageSize: Int = 20): List<Foo> {
    val source = FooSource(query, apolloClient)
    val data = mutableListOf<Foo>()
    val params = PageParams(pageSize = pageSize)
    var hasNext: Boolean

    do {
        val result = runBlocking {
            source.load(params)
        }

        when (result) {
            is LoadResult.Success -> {
                println("Received data: ${result.data.size}")
                data.addAll(result.data)
                params.cursorNext = result.endCursor
                hasNext = result.hasNext
            }
            is LoadResult.Error -> {
                throw result.exception // 也可根据需求返回已加载的部分数据
            }
        }
    } while (hasNext)

    return data
}

更优实现:用 Kotlin Flow 封装分页逻辑

如果想要更优雅的响应式处理,推荐用Flow封装分页流程,避免手动循环:

class FooSource(private val query: String, private val apolloClient: ApolloClient) {

    fun fetchAllPages(pageSize: Int = 20): Flow<Foo> = flow {
        var cursor: String? = null
        do {
            val response = apolloClient.query(
                FooQuery(
                    queryString = query,
                    pageSize = pageSize,
                    cursor = cursor
                )
            ).execute()

            if (response.hasErrors()) {
                throw Exception(response.errors?.joinToString { it.message })
            }

            val fooData = response.data?.foos ?: emptyList()
            fooData.forEach { emit(it) } // 逐个发射数据,也可用emitAll(fooData)批量发射

            cursor = response.data?.pageInfo?.endCursor
        } while (response.data?.pageInfo?.hasNextPage == true)
    }.flowOn(Dispatchers.IO) // 将网络请求切换到IO线程,避免阻塞主线程
}

// 使用示例
fun main() = runBlocking {
    val apolloClient = ApolloClient.Builder()
        .serverUrl("your-graphql-endpoint")
        .build()

    val source = FooSource("your-query-content", apolloClient)
    val allData = source.fetchAllPages().toList()
    println("Total fetched data: ${allData.size}")
}

关键注意事项

  • Apollo版本适配:Apollo 3.x及以上版本中,apolloClient.query(...).execute()是suspend函数,必须在协程或suspend函数中调用,你的runBlocking用法是合法的,但要确保协程上下文正确。
  • 错误处理:除了捕获网络异常,必须处理Apollo返回的GraphQL业务错误(通过response.hasErrors()判断)。
  • 线程调度:命令行应用中runBlocking会阻塞当前线程,用Flow时通过flowOn(Dispatchers.IO)可将网络请求放到IO线程,避免阻塞主线程。

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:15:36