RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1444503
Accepted
user514769
user514769
Asked:2022-08-31 22:03:06 +0000 UTC2022-08-31 22:03:06 +0000 UTC 2022-08-31 22:03:06 +0000 UTC

使用 Retrofit 从 api 获取数据抛出 java.lang.IllegalStateException: 已经执行

  • 772

我正在写一个应用程序“汇率”。该应用程序实现了基础货币之间的切换,据此获得其他货币的汇率。基础货币之间的切换使用Spinner. 当我在启动应用程序后第一次切换到新货币时,一切正常,但是,一旦我返回到我已经收到并显示数据的货币,就会发生错误:

E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.sem.exchangerate, PID: 9209
        java.lang.IllegalStateException: Already executed.
            at retrofit2.OkHttpCall.enqueue(OkHttpCall.java:123)
            at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.enqueue(DefaultCallAdapterFactory.java:78)
            at com.sem.exchangerate.data.dataSourceIMPL.ApiDataSourceIMPL.startMigration(ApiDataSourceIMPL.kt:25)
            at com.sem.exchangerate.data.repository.ExchangeRateRepository.startMigration(ExchangeRateRepository.kt:22)
            at com.sem.exchangerate.domain.useCase.ExchangeRateUseCase.startMigration(ExchangeRateUseCase.kt:21)
            at com.sem.exchangerate.presentation.viewModel.ExchangeRateViewModel$migration$1.invokeSuspend(ExchangeRateViewModel.kt:17)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
            at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
            at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
            at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
            at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
            at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
            at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
            at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
            at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
            at com.sem.exchangerate.presentation.viewModel.ExchangeRateViewModel.migration(ExchangeRateViewModel.kt:16)
            at com.sem.exchangerate.presentation.ExchangeRateFragment$onCreateView$6.onItemSelected(ExchangeRateFragment.kt:102)
            at android.widget.AdapterView.fireOnSelected(AdapterView.java:957)
            at android.widget.AdapterView.dispatchOnItemSelected(AdapterView.java:946)
            at android.widget.AdapterView.access$300(AdapterView.java:55)
            at android.widget.AdapterView$SelectionNotifier.run(AdapterView.java:910)
            at android.os.Handler.handleCallback(Handler.java:938)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:223)
            at android.app.ActivityThread.main(ActivityThread.java:7697)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
            Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@72fd5c2, Dispatchers.Main.immediate]

问题还在于数据每次都被覆盖,这意味着它们需要再次接收(我写这个与java.lang.IllegalStateException: Already executed错误文本中的行有关)

ApiDataSourceIMPL:

class ApiDataSourceIMPL(private val exchangeRateDataSource: ExchangeRateDataSource):
    ApiDataSource {

    companion object {
        var call: Call<ExchangeRateResponseModel>? = null
    }

    override fun startMigration (context: Context, dataApi: Call<ExchangeRateResponseModel>?) {

        call = dataApi
            // ApiClient.instance?.api?.loadExchangeRateApiUSD()
        call?.enqueue(object: Callback<ExchangeRateResponseModel> {
            override fun onResponse(
                call: Call<ExchangeRateResponseModel>,
                response: Response<ExchangeRateResponseModel>
            ) {

                Log.d("ApiDataSource", "onResponse status: ${response.code()}")
                // создаём список
                var loadExchangeRate: ExchangeRateResponseModel? = null
              //  loadExchangeRate?.clear()
                // получаем данные с сервера
                loadExchangeRate = (response.body() as ExchangeRateResponseModel?)!!

                exchangeRateDataSource.insert(ExchangeRateModel(1,"EUR", loadExchangeRate.rates?.EUR))
                exchangeRateDataSource.insert(ExchangeRateModel(2,"AUD", loadExchangeRate.rates?.AUD))
                exchangeRateDataSource.insert(ExchangeRateModel(3,"RUB", loadExchangeRate.rates?.RUB))
                exchangeRateDataSource.insert(ExchangeRateModel(4,"JPY", loadExchangeRate.rates?.JPY))
                exchangeRateDataSource.insert(ExchangeRateModel(5,"MDL", loadExchangeRate.rates?.MDL))

                Toast.makeText(context, "ЗАГРУЗКА", Toast.LENGTH_SHORT).show()
            }

            override fun onFailure(call: Call<ExchangeRateResponseModel>, t: Throwable) {
                Toast.makeText(context, "ОШИБКА! ВКЛЮЧИТЕ ИНТЕРНЕТ!", Toast.LENGTH_SHORT).show()
                Log.e("ApiDataSource2", "onFailure", t)
            }
        })

    }

}

API接口:

interface ApiInterface {

    @Headers("apikey: мой ключ") // вставь сюда ключ с этого сайта https://apilayer.com/marketplace/exchangerates_data-api#documentation-tab
    @GET("exchangerates_data/latest?symbols=AUD%2CEUR%2CJPY%2CMDL%2CRUB&base=USD")
    fun loadExchangeRateApiUSD(): Call<ExchangeRateResponseModel>

    @Headers("apikey: мой ключ") // вставь сюда ключ с этого сайта https://apilayer.com/marketplace/exchangerates_data-api#documentation-tab
    @GET("exchangerates_data/latest?symbols=AUD%2CEUR%2CJPY%2CMDL%2CRUB&base=RON")
    fun loadExchangeRateApiRON(): Call<ExchangeRateResponseModel>

    @Headers("apikey: мой ключ") // вставь сюда ключ с этого сайта https://apilayer.com/marketplace/exchangerates_data-api#documentation-tab
    @GET("exchangerates_data/latest?symbols=AUD%2CEUR%2CJPY%2CMDL%2CRUB&base=GBP")
    fun loadExchangeRateApiGBP(): Call<ExchangeRateResponseModel>

    @Headers("apikey: мой ключ") // вставь сюда ключ с этого сайта https://apilayer.com/marketplace/exchangerates_data-api#documentation-tab
    @GET("exchangerates_data/latest?symbols=AUD%2CEUR%2CJPY%2CMDL%2CRUB&base=KZT")
    fun loadExchangeRateApiKZT(): Call<ExchangeRateResponseModel>
}

ExchangeRateFragment 片段:

class ExchangeRateFragment : Fragment() {

    private var binding: FragmentExchangeRateBinding? = null
    private var exchangeRateAdapter : ExchangeRateAdapter? = null
    private val exchangeRateViewModel : ExchangeRateViewModel? by viewModel()
    private val favouriteViewModel: FavouriteViewModel by viewModel()

    private val dataApi: DataApi? = DataApi()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_exchange_rate, container, false)

        initRecyclerExchangeRate()
        loadExchangeRate()

...

        ArrayAdapter.createFromResource(
            activity?.applicationContext!!,
            R.array.currency_array,
            android.R.layout.simple_spinner_item
        ).also { adapter ->

            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

            binding?.spinner?.adapter = adapter
        }

        binding?.spinner?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) {

                when(position) {
                    0 -> {
                        exchangeRateViewModel?.migration(requireContext(), dataApi?.apiUSD)
                        loadExchangeRate()
                    }
                    1 -> {
                        exchangeRateViewModel?.migration(requireContext(), dataApi?.apiRON)
                        loadExchangeRate()
                    }
                    2 -> {
                        exchangeRateViewModel?.migration(requireContext(), dataApi?.apiGBP)
                        loadExchangeRate()
                    }
                    3 -> {
                        exchangeRateViewModel?.migration(requireContext(), dataApi?.apiKZT)
                        loadExchangeRate()
                    }
                }
            }


            override fun onNothingSelected(p0: AdapterView<*>?) {

            }

        }

        return binding?.root
    }

...

    private fun loadExchangeRate(){

        exchangeRateViewModel?.loadExchange?.observe(viewLifecycleOwner, Observer {

            exchangeRateAdapter?.setList(it)
            exchangeRateAdapter?.notifyDataSetChanged()
        })
    }
...
}
android
  • 1 1 个回答
  • 37 Views

1 个回答

  • Voted
  1. Best Answer
    woesss
    2022-09-01T12:25:02Z2022-09-01T12:25:02Z

    我会做不同的事情,你可以将你的 API 方法通用化为一个以货币标签作为参数的方法,然后一切都变得更简单和更短:

    interface ApiInterface {
    
        @Headers("apikey: мой ключ") // вставь сюда ключ с этого сайта https://apilayer.com/marketplace/exchangerates_data-api#documentation-tab
        @GET("exchangerates_data/latest?symbols=AUD%2CEUR%2CJPY%2CMDL%2CRUB")
        fun loadExchangeRateApi(@Query("base") String currency): Call<ExchangeRateResponseModel>
    }
    
    class ApiDataSourceIMPL(private val exchangeRateDataSource: ExchangeRateDataSource):
        ApiDataSource {
    
        override fun startMigration (context: Context, currency: String?) {
    
            val call = ApiClient.instance?.api?.loadExchangeRateApi(currency)
            call?.enqueue(object: Callback<ExchangeRateResponseModel> {
                override fun onResponse(
                    call: Call<ExchangeRateResponseModel>,
                    response: Response<ExchangeRateResponseModel>
                ) {
    
                    Log.d("ApiDataSource", "onResponse status: ${response.code()}")
                    // создаём список
                    var loadExchangeRate: ExchangeRateResponseModel? = null
                  //  loadExchangeRate?.clear()
                    // получаем данные с сервера
                    loadExchangeRate = (response.body() as ExchangeRateResponseModel?)!!
    
                    exchangeRateDataSource.insert(ExchangeRateModel(1,"EUR", loadExchangeRate.rates?.EUR))
                    exchangeRateDataSource.insert(ExchangeRateModel(2,"AUD", loadExchangeRate.rates?.AUD))
                    exchangeRateDataSource.insert(ExchangeRateModel(3,"RUB", loadExchangeRate.rates?.RUB))
                    exchangeRateDataSource.insert(ExchangeRateModel(4,"JPY", loadExchangeRate.rates?.JPY))
                    exchangeRateDataSource.insert(ExchangeRateModel(5,"MDL", loadExchangeRate.rates?.MDL))
    
                    Toast.makeText(context, "ЗАГРУЗКА", Toast.LENGTH_SHORT).show()
                }
    
                override fun onFailure(call: Call<ExchangeRateResponseModel>, t: Throwable) {
                    Toast.makeText(context, "ОШИБКА! ВКЛЮЧИТЕ ИНТЕРНЕТ!", Toast.LENGTH_SHORT).show()
                    Log.e("ApiDataSource2", "onFailure", t)
                }
            })
    
        }
    
    }
    
    class ExchangeRateFragment : Fragment() {
    
        private var binding: FragmentExchangeRateBinding? = null
        private var exchangeRateAdapter : ExchangeRateAdapter? = null
        private val exchangeRateViewModel : ExchangeRateViewModel? by viewModel()
        private val favouriteViewModel: FavouriteViewModel by viewModel()
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                                  savedInstanceState: Bundle?): View? {
    
            binding = DataBindingUtil.inflate(inflater, R.layout.fragment_exchange_rate, container, false)
    
            initRecyclerExchangeRate()
            loadExchangeRate()
    
    ...
    
            ArrayAdapter.createFromResource(
                activity?.applicationContext!!,
                R.array.currency_array,
                android.R.layout.simple_spinner_item
            ).also { adapter ->
    
                adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
    
                binding?.spinner?.adapter = adapter
            }
    
            binding?.spinner?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) {
    
                    val currency = when(position) {
                        0 -> "USD"
                        1 -> "RON"
                        2 -> "GBP"
                        3 -> "KZT"
                    }
                    exchangeRateViewModel?.migration(requireContext(), currency)
                    loadExchangeRate()
                }
    
    
    
    
                override fun onNothingSelected(p0: AdapterView<*>?) {
    
                }
    
            }
    
            return binding?.root
        }
    
    ...
    
        private fun loadExchangeRate(){
    
            exchangeRateViewModel?.loadExchange?.observe(viewLifecycleOwner, Observer {
    
                exchangeRateAdapter?.setList(it)
                exchangeRateAdapter?.notifyDataSetChanged()
            })
        }
    ...
    }
    

    PS注意现在多加几个货币会容易得多,如果你把它们的标签安排到一个string-array资源中,那么你甚至不必为此编辑类代码,只需将名称和标签添加到资源。

    • 0

相关问题

  • 来自片段的列表落后于 BottomNavigationView

  • 无法将变量从 Activity 传递到 Fragment

  • 构建与完成的片段略有不同的片段的最佳方法是什么?

  • 如何更改来自服务器的响应中的日期格式?

  • 谷歌地图在应用程序的发布版本中不起作用

  • 材料设计按钮。单击按钮上的可选区域!

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5