【问题标题】:Extension function not creating new Observable object扩展函数未创建新的 Observable 对象
【发布时间】:2017-09-28 03:27:24
【问题描述】:

我在使用 kotlin 和 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 调用的observable 都是相同的。 我需要他们有自己的 observable,因为如果我不这样做,当retryWhen 失败时,它不会继续下一次失败。我希望它有意义。

将 observable 放入 deferflatmap 不会改变任何东西。

编辑我的代码

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 完成从服务器获取数据后触发。

【问题讨论】:

    标签: android kotlin picasso rx-java2 kotlin-extension


    【解决方案1】:

    你必须在这里小心。关键字this in

    Log.d("picasso load bitmap", "me ${this}")
    

    不针对Observable,而是针对接收者类型。在你的情况下Picasso。你在你的日志里看到了me com.squareup.picasso.Picasso@c894e26

    好消息是,每次调用 loadBitmap 都会获得一个新的 Observable 实例。您可以通过以下方式检查:

    val observable = picasso.loadBitmap(place.image_url)
    Log.d("observable for picasso", "$observable")
    observable.subscribeOn(Schedulers.io())...
    

    所以你看,你总是在picasso 的同一个实例上调用loadBitmap,这就是你为那个类获得相同输出的原因。但是每次调用loadBitmap 都会创建一个新的Observable

    所以你的代码没问题。

    【讨论】:

    • 谢谢,但是为什么我只能获得1个重试周期,仅此而已?我只能得到 1 个加载错误,但我还有很多要加载的 url。
    • 你能补充一些关于这个问题的更多细节吗?
    • 抱歉,我有一个从服务器获取的地点列表。这个地方有 img_urls,我需要在每个地方做一个标记,同时生成一个 observable 来加载图像。发生的情况是,当我获得重试周期时,它不会继续加载其余的 img。我希望它有意义。我会发布代码。
    • 只是为了缩小问题范围。你能用例如替换你的整个retryWhen吗? .doOnError { Log.d("Error received") }。如果这有帮助,那么您必须检查retryWhen
    • 嗨@guenhter,我现在试过了,我仍然得到1 java.io.IOException: Failed to decode stream.,仅此而已。
    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 2014-11-18
    • 1970-01-01
    • 2013-11-01
    相关资源
    最近更新 更多