【问题标题】:Retrofit Single<T> blocking UI thread改造 Single<T> 阻塞 UI 线程
【发布时间】:2019-03-21 07:33:20
【问题描述】:

使用单块 UI 线程改进第一个请求。下面是相关代码,还有更多文字:

改造提供者

object RetrofitProvider {

private val TAG: String = RetrofitProvider::class.java.simpleName

val retrofit: Retrofit by lazy {
    val httpClient = OkHttpClient.Builder()
        .addInterceptor {
            val request = it.request()
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "${request.method()}: ${request.url()}")
            }

            it.proceed(request)
        }
        .build()

    Retrofit.Builder()
        .client(httpClient)
        .baseUrl("http://192.168.0.10:3000")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
        .addConverterFactory(JacksonConverterFactory.create(jacksonObjectMapper()))
        .build()
}
}

产品接口

interface ProductApi {

@GET("/products")
fun getProducts(): Single<List<Product>>

}

主视图模型

    fun fetchProducts() {
    productData.value = Resource.Loading()
    productApi.getProducts() // <- This call is a problem (even when I comment out all code below)
        .subscribeOn(Schedulers.io())
        .subscribe(
            {
                productData.postValue(Resource.Success(it))
            },
            {
                productData.postValue(Resource.Fail(it.message))
            })
        .addTo(disposableContainer)
}

主片段

...
        button.setOnClickListener {
        Toast.makeText(requireContext(), "click", Toast.LENGTH_SHORT).show()
        mainViewModel.fetchProducts()
    }
...

应用流程很简单,点击 MainFragment 上的按钮会调用 MainViewModel 的 fetchProducts(),它使用改造来获取一些东西。

productApi.getProducts() 发生在 UI 线程上并显着阻塞它(~半秒),即使 Toast 被延迟,即使它应该在按钮单击时立即显示,在 之前getProducts() 调用。

productApi.getProducts() 本身,没有 subscribe 不会发送网络请求(我在服务器端检查过),它只是准备 Single。

重要提示,后续点击按钮不会发生延迟。只是第一次,我猜创建 Single 是昂贵的操作。

所以我的问题是,为什么 UI 线程在第一次请求时被阻止,以及如何修复它而不是丑陋/黑客攻击。

Observable 的作用也一样,但 Completable 工作得更快,但我需要数据,所以不能使用 Completable。

【问题讨论】:

  • 您是否尝试在subscribeOn(...) 方法之后添加.observerOn(AndroidSchedulers.main())
  • 是的,没用。
  • 你能在 Android Studio 中使用 CPU 分析器来看看什么方法需要这么长时间吗?

标签: android kotlin retrofit rx-java2


【解决方案1】:

我认为您的问题在于您的 Retrofit 对象的延迟初始化。 它将被推迟到最后一刻,所以我猜你第一次点击按钮时,你创建了昂贵的改造按钮(这是在主线程上完成的)。

我的建议是删除延迟初始化并再次尝试运行应用程序。

【讨论】:

    【解决方案2】:

    返回 Completable 也会阻塞 UI 线程,但比返回 SingleObservable 的时间短,所以看起来它没有任何影响,但确实有。

    在后台线程上调用 API 不会阻塞您的 UI,因为转换器创建不会发生在 UI 线程上。

    这样的事情可以解决问题。

        Completable.complete()
            .observeOn(Schedulers.io())
            .subscribe {
                productApi.getProducts()
                    .subscribe(
                        {
                            productData.postValue(Resource.Success(it))
                        },
                        {
                            productData.postValue(Resource.Fail(it.message))
                        }
                    )
                    .addTo(disposableContainer)
            }
            .addTo(disposableContainer)
    

    除了使用转换器之外,您可以做的另一件事是围绕 Retrofit API 创建一个包装类,该类将在后台线程上的合适的 observable 中调用它。

        fun getProducts() = Single.create<List<Product>> { emitter ->
            try {
                val response = productApi.getProducts().execute()
                if (!response.isSuccessful) {
                    throw HttpException(response)
                }
    
                emitter.onSuccess(response.body()!!)
            } catch (e: Exception) {
                emitter.onError(e)
            }
        }.observeOn(Schedulers.io())
    

    【讨论】:

      【解决方案3】:

      当您调用 RxJava 操作时,例如,一个改造请求,您可以告诉它在哪里执行操作以及从哪里获取结果,默认位置是您订阅它的位置 为了改变它,你需要添加两行

      observeOn(Where you will receive the result)
      subscribeOn(Where the action will be executed)
      

      在你的情况下,应该是这样的

      productApi.getProducts() // <- This call is a problem (even when I comment out all code below)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(Schedulers.io()) //or .subscribeOn(Schedulers.newThread())
        .subscribe({Success},{Failure})
      

      我制作了一个library,其中包含许多用于在 kotlin 中进行 Android 开发的实用程序/扩展。

      其中一个软件包可让您轻松避免此问题。

      您只需输入:

      yourObservable //or any other reactive type
         .runSafeOnMain() //it will perform you action in another thread and it will return the result in main
         .subscribe({}, {])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多