【问题标题】:How to minimize the number of webservice calls using Kotlin coroutines?如何使用 Kotlin 协程最小化 Web 服务调用的数量?
【发布时间】:2020-10-01 04:25:45
【问题描述】:

在我的 Android Kotlin 项目中,我在协程中调用了一个 Web 服务(myWebservice 只是一个管理 Web 服务调用的自定义类):

fun searchForItems(userInput: String)
{
    CoroutineScope(Dispatchers.IO + Job()).launch {
        val listOfItems = myWebService.call(userInput)
    }
}

每当用户在EditText 中键入字符时,都会调用该方法,因此应用程序会调用一个 Web 服务,该服务会返回与他的请求匹配的项目列表。但我想优化它。

假设用户输入了单词:“apple”。为了尽量减少webservice调用的次数,下面是我想要实现的:

  • 当用户键入第一个字母 (a) 时,将调用 Web 服务
  • 当用户输入下一个字母时,只要第一个调用没有返回,就没有新的 Web 服务调用(假设他有足够的时间输入下一个字母 (pple))
  • 当第一次 web 服务调用完成时,新的调用会自动使用新的用户输入(苹果)完成

实现这一目标的最佳做法是什么? 或者有没有更好的方法来减少 Web 服务调用的次数?

谢谢。

【问题讨论】:

    标签: android kotlin kotlin-coroutines debouncing


    【解决方案1】:

    使用 Kotlin 协程我解决了这个问题:

    class SomeViewModel : ViewModel() {
        private var searchJob: Job? = null
    
        fun search(userInput: String) {
            searchJob?.cancel() // cancel previous job when user enters new letter
            searchJob = viewModelScope.launch {
                delay(300)      // add some delay before search, this function checks if coroutine is canceled, if it is canceled it won't continue execution
                val listOfItems = myWebService.call(userInput)
                ...
            }
        }  
    }
    

    当用户输入第一个字母时,search()函数被调用,协程被启动并且这个协程的Job被保存到searchJob。然后调用delay(300) 函数等待另一个用户输入,然后再调用WebService。如果用户在 300 毫秒过期前输入另一个字母,search() 函数将被再次调用,之前的协程将使用 searchJob?.cancel() 函数取消,并且不会在第一个协程中调用 WebService。

    【讨论】:

      【解决方案2】:

      您需要去抖动。协程与否,您不应该为每个活动用户调用每个字母的 Web 服务。如果您的应用同时被许多人使用,那最终会 DDOS 网络服务。

      由于您使用的是 Kotlin,因此您可以使用 Flow 来代替协程。它带有一个内置的debounce 方法。字母流也很容易建模为流。它会是这样的(我不确定这是否会运行,但你明白了):

      textFlow = flow {
          myTextView.doOnTextChanged { text, start, count, after -> emit(text)}
      }.debounce(1000)
      

      更复杂的替代方案是 RxJava 的 debounce operator

      您也可以使用 lupajz's debounce extension 尝试 LiveData

      或者您也可以推出自己的解决方案。

      【讨论】:

      • 谢谢!我同意,我在想Flow。也就是说,我不明白如何将它绑定到用户输入事件。您能否提供一个示例或链接?
      • 为 Flow 添加了 sn-p
      • 好吧,我必须使用自定义解决方案,但我学到了一些新东西,所以谢谢!
      【解决方案3】:

      您可以使用 Rx Java 的 debounce 运算符实现相同的功能。每次您在 Edit Text Rx 中输入文本时,Java 的 debounce 都会调用 web 服务并迅速产生结果,如果用户再次输入另一个文本,它将调用 web 服务。

      请参考以下链接了解面糊,这样你就可以修改你的代码并实现相同的功能

      https://blog.mindorks.com/implement-search-using-rxjava-operators-c8882b64fe1d

      RxSearchObservable.fromView(searchView)
              .debounce(300, TimeUnit.MILLISECONDS)
              .filter(new Predicate<String>() {
                  @Override
                  public boolean test(String text) {
                      if (text.isEmpty()) {
                          textViewResult.setText("");
                          return false;
                      } else {
                          return true;
                      }
                  }
              })
              .distinctUntilChanged()
              .switchMap(new Function<String, ObservableSource<String>>() {
                  @Override
                  public ObservableSource<String> apply(String query) {
                      return dataFromNetwork(query)
                              .doOnError(throwable -> {
                                  // handle error
                              })
                              // continue emission in case of error also
                              .onErrorReturn(throwable -> "");
                  }
              })
              .subscribeOn(Schedulers.io())
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(new Consumer<String>() {
                  @Override
                  public void accept(String result) {
                      textViewResult.setText(result);
                  }
              });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-21
        • 2021-01-26
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 2020-01-10
        • 2018-02-07
        • 1970-01-01
        相关资源
        最近更新 更多