在 Adapter for Place API 的 Filter.performFiltering() 中使用 Rx debounce 运算符

Posted

技术标签:

【中文标题】在 Adapter for Place API 的 Filter.performFiltering() 中使用 Rx debounce 运算符【英文标题】:Using Rx debounce operator in Filter.performFiltering() in Adapter for Place API 【发布时间】:2019-12-07 14:31:16 【问题描述】:

我们的应用具有用于搜索地点的 AutoCompleteTextView,以及 Google Places API。

我想添加 debounce RxJava OPERATOR 来进行搜索。我不太擅长 RxJava。

Adapter 已实现 getFilter(),Filter 已覆盖 performFiltering(),以字符串形式接收约束/查询。 过滤器目前看起来像这样:

private inner class AutocompleteFilter : Filter() 

    @WorkerThread
    override fun performFiltering(constraint: CharSequence?): Filter.FilterResults 
        val results = Filter.FilterResults()
        val search = constraint?.toString() ?: ""

        var found = emptyList<PlaceData>()

        // quick reply on empty search
        val bounds = latLngBounds
        if (!TextUtils.isEmpty(search) && bounds != null) 
            found = placesApiHelper.fetchAutocompletePredictions(search, bounds)
                    .timeout(1000L, TimeUnit.MILLISECONDS)
                    .toList()
                    .doOnError 
                        searchResultSubject.onNext(false)
                        logTo(log).warning("Timeout or error happens. %s", it)
                    
                    .doOnSuccess 
                        searchResultSubject.onNext(true)
                    
                    .onErrorReturn  emptyList() 
                    .blockingGet()
        

        results.values = found
        results.count = found.size

        return results
    

    @MainThread
    override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults) 
        logTo(log).info("publishing results. Found: %s items", results.count)

        data.clear()

        val list = results.values as? List<*>
        list?.let 
            val values = it.filterIsInstance<PlaceData>()
            if (values.isNotEmpty()) 
                data.addAll(values)
                notifyDataSetChanged()
            
        
    

而 fetchAutoCompletePredictions() 看起来像这样:

 private fun fetchAutocompletePredictions(query: String, latLngBounds: LatLngBounds): Observable<PlaceData> 
    Timber.d("places api request: %s (total: %s, cached: %s)", query, PlacesApiCache.CALLS.incrementAndGet(), PlacesApiCache.HITS.get())

    val rectangularBounds = toRectangularBounds(latLngBounds)

    val request = FindAutocompletePredictionsRequest.builder()
            .setLocationBias(rectangularBounds)
            .setQuery(query)
            .build()

    return Observable.create 
        placesClient.findAutocompletePredictions(request).addOnSuccessListener  response ->

            val predictions = response.autocompletePredictions
            val results = mutableListOf<PlaceData>()

            for (i in 0 until Math.min(predictions.size, MAX_RESULTS)) 
                results.add(placeDataFrom(predictions[i]))
            

            PlacesApiCache.cacheData(query, results)

            for (result in results) 
                it.onNext(result)
            

            it.onComplete()
        .addOnFailureListener  exception ->
            if (exception is ApiException) 
                Timber.e("Place not found: %s", exception.getStatusCode())
            
            it.onError(exception)
        
    

我尝试使用 PublichSubject(来自 JakeWharton 库的 PublishRelay),但我仍然没有信心修复它,因为 EVENT 通过此调用(获取约束)在这里发生,并且相同的方法也应该返回 FIlterResult。这意味着观察者也应该放在 performFiltering() 中。这意味着对于每个字母条目,此方法都会命中多个观察者。 而且,我应该使用与新搜索查询(约束)相同的方法来调用 subject.onNext() 仅在此处知道。

在这种情况下如何使用 debounce 运算符使整个过程同步,最后返回 FIlterResults ?

提前致谢!

【问题讨论】:

我希望 Places API 构建器有助于消除抖动并为应用程序做这些事情。它不这样做。当然,他们为什么要鼓励。让应用调用API,增加成本。 :) 【参考方案1】:

我去掉了过滤器。我没有让过滤器回调收到有关新搜索字符串的通知,而是添加了自己的 publishSubject 来观察新的搜索字符串并通知观察者。

自己的观察者(使用 publicSubject 而不是过滤器回调)允许我在流中插入去抖动运算符。

在适配器中添加了这个:

private var queryPlacesSubject = PublishSubject.create<String>()

init 
    subscribeForPlacePredictions()


private fun subscribeForPlacePredictions() 
    queryPlacesSubject
            .debounce(DEBOUNCE_TIME_MILLIS, TimeUnit.MILLISECONDS)
            .distinctUntilChanged()
            .observeOn(Schedulers.newThread())
            .doOnNext  findPlacePredictions(it) 
            .observeOn(androidSchedulers.mainThread())
            .doOnNext notifyThePlacesChanged()
            .subscribeOn(Schedulers.io())
            .subscribe()


fun setQuery(query: String) 
    queryPlacesSubject.onNext(query)

那么这两个在adapter中:

    private fun findPlacePredictions(query: String?) 
    val search = query ?: ""

    // save entered text by user for highlight operations
    highlight = search

    // we should be in background thread already
    var found = emptyList<PlaceData>()

    // quick reply on empty search
    val bounds = latLngBounds
    if (!TextUtils.isEmpty(search) && bounds != null) 
        found = placesApiHelper.requestAutocomplete(search, bounds)
                .timeout(runtime.timeoutAutocompleteSearch(), TimeUnit.MILLISECONDS)
                .toList()
                .doOnError 
                    searchResultSubject.onNext(false)
                    logTo(log).warning("Timeout or error happens. %s", it)
                
                .doOnSuccess 
                    searchResultSubject.onNext(true)
                
                .onErrorReturn  emptyList() 
                .blockingGet()
    

    data.clear()
    data.addAll(found)


private fun notifyThePlacesChanged() 
    notifyDataSetChanged()

已将新的搜索字符串设置为我的演示者的主题:

adapter.setQuery(currText)

【讨论】:

以上是关于在 Adapter for Place API 的 Filter.performFiltering() 中使用 Rx debounce 运算符的主要内容,如果未能解决你的问题,请参考以下文章

使用 Google Place API 获取事件

使用 Google Place API 自动完成 TextView

在 .each for 循环中选择 rest_in_place

适用于 Android 的 Google Place API:无法使用 Place Picker 的选择器选择地点

用于从 Google for business 获取所有评论和评级的 API

谷歌地图api place_changed事件不是射击