You need to enable JavaScript to run this app. 如何实现Retrofit顺序执行Upbit两次API请求解决异步时序问题
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Retrofit顺序执行Upbit两次API请求解决异步时序问题

Upbit 串行请求实现方案

当前实现的核心问题

你现有代码的逻辑错位来自三个核心设计缺陷,和Retrofit本身的异步机制特性直接相关:

  • 时序不匹配:Retrofit的enqueue()是非阻塞异步方法,调用后不会等待请求返回就直接继续执行后续代码,导致第二次ticker请求发出时,第一次请求的返回值还没到达,传入的markets参数始终为空
  • 变量作用域混乱:getMarketsUpbit()内部单独声明了一个局部的var markets,完全遮蔽了外部全局的同名字段,就算第一次请求解析出了正确的交易对字符串,也不会被后续的ticker请求使用
  • 状态同步链路断裂:用PublishSubject做值传递的设计,只是在第一次请求返回时异步更新全局变量,完全没有控制第二次请求的触发时机,本质上和手动给全局变量赋值没有区别,无法解决异步时序差的问题
  • 资源冗余:每次请求、每个交易所都重新新建Retrofit实例,既没必要也会增加额外的性能开销。

可落地的实现方案

方案1:嵌套回调(无额外依赖,改造成本最低)

核心思路是把第二次请求的触发逻辑放到第一次请求的成功回调里,从机制上保证第二次请求一定会拿到第一次的返回值:

// 全局复用单个Retrofit实例,不要重复构建
private val upbitService = Retrofit.Builder()
    .baseUrl("https://api.upbit.com/v1/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
    .create(RetrofitService::class.java)
private val parser = DataParser()

fun getData(exchange: String) {
    // 其他交易所的原有逻辑保持不变
    when(exchange) {
        coinone -> fetchCoinoneData()
        bithumb -> fetchBithumbData()
        huobi -> fetchHuobiData()
        // Upbit走单独的串行请求逻辑
        upbit -> fetchUpbitData()
    }
}

private fun fetchUpbitData() {
    // 第一步:先请求全量交易对列表
    upbitService.getMarketsUpbit().enqueue(object : retrofit2.Callback<Any> {
        override fun onResponse(call: retrofit2.Call<Any>, response: retrofit2.Response<Any>) {
            if (!response.isSuccessful) {
                println("Upbit交易对列表请求失败")
                return
            }
            // 解析得到拼接好的markets查询参数
            val marketsParam = parser.parseUpbitMarkets(response.body().toString())

            // 第二步:拿到markets参数后,再发起行情请求
            upbitService.getTickersUpbit(marketsParam).enqueue(object : retrofit2.Callback<Any> {
                override fun onResponse(call: retrofit2.Call<Any>, response: retrofit2.Response<Any>) {
                    if (!response.isSuccessful) {
                        println("Upbit行情请求失败")
                        return
                    }
                    // 解析最终结果
                    coinInfos.value = parser.getParsedData(
                        marketsParam,
                        response.body().toString()
                    )
                }

                override fun onFailure(call: retrofit2.Call<Any>, t: Throwable) {
                    println("Upbit行情请求失败:${t.message}")
                }
            })
        }

        override fun onFailure(call: retrofit2.Call<Any>, t: Throwable) {
            println("Upbit交易对列表请求失败:${t.message}")
        }
    })
}

方案2:协程实现(推荐,代码简洁无嵌套)

Android官方推荐的协程方案,可以用接近同步代码的写法实现异步串行调用,完全避免时序错位:

  1. 先给Retrofit接口的方法加上suspend修饰,Retrofit会自动处理协程的挂起逻辑
    interface RetrofitService {
        @GET("market/all")
        suspend fun getMarketsUpbit(): Any
    
        @GET("ticker")
        suspend fun getTickersUpbit(@Query("markets") markets: String): Any
    }
    
  2. 用协程作用域执行请求流程:
    private fun fetchUpbitData() {
        // 在对应生命周期的协程作用域内执行,比如viewModelScope
        viewModelScope.launch {
            try {
                // 挂起等待第一个请求返回
                val marketsResponse = upbitService.getMarketsUpbit()
                val marketsParam = parser.parseUpbitMarkets(marketsResponse.toString())
    
                // 拿到参数后执行第二个请求
                val tickerResponse = upbitService.getTickersUpbit(marketsParam)
                coinInfos.value = parser.getParsedData(marketsParam, tickerResponse.toString())
            } catch (e: Exception) {
                println("Upbit请求流程失败:${e.message}")
            }
        }
    }
    

方案3:RxJava链式实现(适配现有RxJava技术栈)

如果已经引入了RxJava,不需要再用全局变量和PublishSubject做状态同步,直接用操作符把两次请求串成完整的链路:

upbitService.getMarketsUpbit()
    .flatMap { marketsResp ->
        val marketsParam = parser.parseUpbitMarkets(marketsResp.toString())
        // 第一次请求成功后触发第二次请求,透传解析好的markets参数
        upbitService.getTickersUpbit(marketsParam).map { tickerResp ->
            marketsParam to tickerResp
        }
    }
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { (marketsParam, tickerResp) ->
            coinInfos.value = parser.getParsedData(marketsParam, tickerResp.toString())
        },
        { error ->
            println("Upbit请求失败:${error.message}")
        }
    )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 22:27:00