Модификация Single <T>блокировка потока пользовательского интерфейса - PullRequest
0 голосов
/ 16 октября 2018

Модифицируйте первый запрос с потоком пользовательского интерфейса для отдельных блоков.Ниже приведен соответствующий код и текст:

RetrofitProvider

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

ProductApi

interface ProductApi {

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

}

MainViewModel

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

MainFragment

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

Поток приложений прост, нажатие кнопки на MainFragment вызывает метод fetchProducts () MainViewModel, который использует модификацию для извлечения некоторых вещей.

productApi.getProducts () происходит в потоке пользовательского интерфейса изначительно блокирует его (~ полсекунды), даже тост задерживается, хотя он должен отображаться сразу после нажатия кнопки, прежде чем getProducts () вызов.

productApi.getProducts() сам по себе, без подписка не отправляет сетевой запрос (я проверял на стороне сервера), он просто готовит Single.

Важное замечание, задержка НЕ ​​происходит при последующихнажимает на кнопку.Просто в первый раз, я думаю, что создание Single <> - дорогая операция.

Поэтому мой вопрос: почему поток пользовательского интерфейса блокируется при первом запросе, и как мне это исправить, чтобы он не был уродливым или хакерским?.

Также Observable действует так же, но Completable работает намного быстрее, но мне нужны данные, поэтому я не могу использовать Completable.

Ответы [ 3 ]

0 голосов
/ 18 октября 2018

Возвращение Completable также блокирует поток пользовательского интерфейса, но на меньшее время, чем возвращение Single или Observable, поэтому кажется, что это не оказывает никакого влияния, но это имеет значение.

Вызов вызова APIв фоновом потоке не будет блокировать ваш пользовательский интерфейс, так как создание потока не будет происходить в потоке пользовательского интерфейса.

Что-то подобное делает свое дело.

    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())
0 голосов
/ 18 октября 2018

Когда вы вызываете действие 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})

Я сделал библиотеку , которая имеет множество утилит / расширений дляРазработка Android в kotlin.

Один из пакетов предназначен для того, чтобы было легко избежать этой проблемы.

Все, что вам нужно сделать, это набрать:

yourObservable //or any other reactive type
   .runSafeOnMain() //it will perform you action in another thread and it will return the result in main
   .subscribe({}, {])
0 голосов
/ 16 октября 2018

Я думаю, что ваша проблема заключается в ленивой инициализации вашего объекта Retrofit.Это будет отложено до последнего возможного момента, поэтому я думаю, что при первом нажатии на кнопку вы создаете дорогую кнопку дооснащения (это делается в главном потоке).

Я предлагаю удалить отложенную инициализацию и попробовать запустить приложение еще раз.

...