У меня неожиданное поведение с котлин и rxjava. Я создаю функцию расширения для загрузки изображения с помощью пикассо
fun Picasso.loadBitmap(url: String) : Observable<Bitmap> = Observable.create<Bitmap> { emitter -> Log.d("picasso load bitmap", "me ${this}") try { val bitmap = load(url).centerCrop() .resize(100, 100) .transform(CircleTransformer()) .get() emitter.onNext(bitmap) emitter.onComplete() } catch (e: IOException) { emitter.onError(e) } }
Я называю это несколько раз с близким интервалом (почти в одно и то же время), как это,
picasso.loadBitmap(place.image_url) .subscribeOn(Schedulers.io()) .retryWhen { error -> error.zipWith(Observable.range(1, 5), BiFunction<Throwable, Int, RetryWrapper> { t1, t2 -> RetryWrapper(t2.toLong(), t1) }) .flatMap { if(it.delay < 4){ Log.d(TAG, "retry no. ${it.delay} for ${place.image_url}") Observable.timer(it.delay * 5, TimeUnit.SECONDS) } else { Log.d(TAG, "DMD ${place.image_url}") Observable.error { it.error } } } } .subscribe ( { bitmap -> markers.find { it.place.id == place.id }?.let { it.marker.icon = IconFactory.getInstance(context).fromBitmap(bitmap) } }, { Log.e(TAG, "error decoding ${place.image_url}", it) })
Я ожидаю, что каждый раз, когда loadBitmap
будет вызван, он создаст новое наблюдаемое. Но я получил это в журналах
09-28 11:17:00.022 31694-32276/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.068 31694-32277/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.069 31694-31959/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.108 31694-32278/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.112 31694-32251/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.125 31694-32260/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.162 31694-31794/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.192 31694-32280/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.195 31694-32279/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:00.219 31694-32281/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:04.828 31694-32262/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:14.885 31694-31793/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26 09-28 11:17:29.928 31694-32269/? D/picasso load bitmap: me com.squareup.picasso.Picasso@c894e26
Наблюдаемый одинаковый для всех вызовов loadBitmap
. Мне нужно, чтобы у них было собственное наблюдение, потому что, если я не буду, когда retryWhen
не удастся, он не retryWhen
к следующему сбою. Надеюсь, это имеет смысл.
Помещение наблюдаемого внутри defer
или flatmap
ничего не изменит.
ИЗМЕНИТЬ мой код
override fun render(state: MainState) { map?.let { map -> val newMarkers: MutableList<PlaceMarker> = mutableListOf() for(place in state.places) { var placeMarker = placeMarkers.find { it.place.id == place.id } if(placeMarker != null && map.markers.contains(placeMarker.marker)) { newMarkers.add(placeMarker) placeMarkers.remove(placeMarker) } else { if(placeMarker != null) placeMarkers.remove(placeMarker) val option = MarkerOptions() option.position = LatLng(place.latitude, place.longitude) option.snippet = place.name placeMarker = PlaceMarker(place, map.addMarker(option)) newMarkers.add(placeMarker) picasso.loadBitmap(place.image_url) .subscribeOn(Schedulers.io()) .retryWhen { error -> error.zipWith(Observable.range(1, 5), BiFunction<Throwable, Int, RetryWrapper> { t1, t2 -> RetryWrapper(t2.toLong(), t1) }) .flatMap { if(it.delay < 4){ Log.d(TAG, "retry no. ${it.delay} for ${place.image_url}") Observable.timer(it.delay * 5, TimeUnit.SECONDS) } else { Log.d(TAG, "DMD ${place.image_url}") Observable.error { it.error } } } } .subscribe ( { bitmap -> placeMarkers.find { it.place.id == place.id }?.let { it.marker.icon = IconFactory.getInstance(context).fromBitmap(bitmap) bitmap.recycle() } }, { Log.e(TAG, "error decoding ${place.image_url}", it) }) } } placeMarkers.forEach { it.marker.remove() } placeMarkers.clear() placeMarkers.addAll(newMarkers) } }
Я использую MVP, просто для вас, чтобы увидеть немного шире. Таким образом, это функция внутри VIEW, рендеринг будет запущен после того, как MODEL завершит получение данных с сервера.
Вы должны быть осторожны. Ключевое слово this
в
Log.d("picasso load bitmap", "me ${this}")
не ориентируется на Observable
но тип приемника. В вашем случае Picasso
. Вы видите, что в вашем журнале me com.squareup.picasso.Picasso@c894e26
Хорошей новостью является то, что вы получаете новый экземпляр Observable
для каждого вызова loadBitmap
. Вы можете проверить это:
val observable = picasso.loadBitmap(place.image_url) Log.d("observable for picasso", "$observable") observable.subscribeOn(Schedulers.io())...
Итак, вы видите, вы вызываете loadBitmap
всегда на одном экземпляре picasso
, поэтому вы получаете тот же результат для этого класса. Но каждый индивидуальный вызов loadBitmap
создает новый Observable
Так что ваш код в порядке.