改造单<T>阻塞 UI 线程



使用单块 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((本身,没有订阅不会发送网络请求(我在服务器端检查过(,它只是准备单。

重要提示,延迟不会在随后单击按钮时发生。只是第一次,我想创建Single<>是昂贵的操作。

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

Observable的行为也相同,但Completable的工作速度要快得多,但我需要数据,所以不能使用Completable。

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

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

返回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 创建一个包装类,该包装类将在后台线程上可观察的拟合中调用它。

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())

当您调用 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})

我制作了一个库,其中包含许多用于 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({}, {])

相关内容

  • 没有找到相关文章

最新更新