如何处理以前的请求并停止观察以前的 LiveData

Posted

技术标签:

【中文标题】如何处理以前的请求并停止观察以前的 LiveData【英文标题】:How to dispose previous requests and stop observing the previous LiveData 【发布时间】:2020-04-11 00:11:49 【问题描述】:

我正在尝试实现一个有 searchView 的应用程序,当用户搜索某些内容时,应用程序将调用一个返回可观察数据的改造请求。在 myRepository 中,我将 observable 转换为 flowable,在 myViewModel 中,我将 flowable 转换为我在 myActivity 中观察到的 LiveData。

但问题是,如果用户搜索不止一次并且非常快(在获得上一个结果之前),那么我想取消以前的请求并且也不想观察以前的数据。

所以我使用的代码如下:

MainActivity

class MainActivity  : DaggerAppCompatActivity() 

 override fun onCreate(savedInstanceState: Bundle?) 
        observeRepos()
 

 fun getReposFromServer(filter_search :String )    
        mainViewModel.getReposFromServer(filter_search)    

 private fun observeRepos() 
        mainViewModel.observeReposFromServer().observe(this, Observer  repos ->

            txtVwCount.setText("total item count is: "+repos?.total_count.toString())

        )

        mainViewModel.observeItemList().observe(this, Observer 

            if(!it.isNullOrEmpty())
                if(it.size>0) 
                    mAdapter.setReposInAdapter(it)
                    progressBar.visibility = View.GONE
                
        )
    


override fun onCreateOptionsMenu(menu: Menu?): Boolean 
        .....
        ......
         searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener

          override fun onQueryTextSubmit(query: String?): Boolean 
                query?.let 
                            if(it.trim().length>0) 
                                clearOldCalls()
                                getReposFromServer(it,"","")      
                            
                        return false
            

            override fun onQueryTextChange(newText: String?): Boolean 
                     return false;          

        )
        return true
    


fun clearOldCalls()   
        mainViewModel.clearRetrofitCall()
        mAdapter.clearListInAdapter() //in my adapter I just make the list empty by assigning a new list to it
    


MainViewModel:

class MainViewModel @Inject constructor() : ViewModel() 
    var liveGitResult = MediatorLiveData<GitResult>()
    val liveItemList =  MediatorLiveData<MutableList<ItemList>>()

     @set:Inject
    lateinit var mainRepository: MainRepository

    fun getReposFromServer(filter_search: String)    
          val resultFromApiCall_flowable : Flowable<GitResult> =  mainRepository.fetchToDosFromServer(filter_search)
         lateinit var source: LiveData<GitResult>

         resultFromApiCall_flowable.let  
                 source = LiveDataReactiveStreams.fromPublisher(it)

                  liveGitResult.addSource(source)  todos ->
                       liveGitResult.setValue(todos)
                       liveGitResult.removeSource(source)   

            

         var itemList_observable = resultFromApiCall_flowable.map //it = gitResult
                    gitResult ->
                        var lst = mutableListOf<ItemList>()
                        gitResult.items.forEach 
                                                    lst.add(it)   

                     lst
            

        itemList_observable?.let
                        var liveItemList  = LiveDataReactiveStreams.fromPublisher(itemList_observable)

                        this.liveItemList.addSource(liveItemList) itemList ->
                        this.liveItemList.setValue(itemList)
                        this.liveItemList.removeSource(liveItemList)    
            


     fun observeReposFromServer(): LiveData<GitResult> 
        return liveGitResult
    

    fun observeItemList(): LiveData<MutableList<ItemList>> 

        return liveItemList
    

    fun clearRetrofitCall()
    
        liveGitResult.value =null
        liveItemList.value = null
        mainRepository.clearDisposables()
    


MainRepository:

class MainRepository @Inject constructor(mainApi: MainApi) 

    private val mainApi: MainApi
    private val disposables: CompositeDisposable = CompositeDisposable()


    init 
        this.mainApi = mainApi
    

    fun fetchToDosFromServer(filter_search: String) : Observable<GitResult> 
 lateinit var  returnedData : Observable<GitResult>

      //mainApi.getAllRepo(filter_search) is a retrofit call which returns a   Flowable<GitResult>
             returnedData =    mainApi.getAllRepo(filter_search).subscribeOn( Schedulers.io())
                                                             .onErrorReturn(Function throwable ->
                                                                Log.e( LOG_TAG, "Something went wrong" )
                                                                 null
                                                             )


              returnedData.subscribeOn(Schedulers.io())
                          .observeOn( androidSchedulers.mainThread())
                          .subscribe(object :Observer<GitResult>
                                                override fun onSubscribe(d: Disposable) 
                                                    disposables.add(d)  

                                                override fun onComplete()  

                                                override fun onNext(t: GitResult)  

                                                override fun onError(e: Throwable)  

                                            )

            return returnedData.toFlowable(BackpressureStrategy.BUFFER);

    


     fun clearDisposables()
        if(disposables!=null)
            if(!disposables.isDisposed)
                disposables.clear()
                disposables.dispose()
            
    


现在,如果我在获得上一个结果之前运行应用程序并快速搜索多次 - 那么它的行为很奇怪..

它在recyclerview 中一一显示所有结果。之前的结果不应该被处理掉并且不会转换为 liveData 吗? 也不是按顺序调用它们 此外,没有同时显示 recyclerview 数据和 textView 数据,可能使用来自不同 Livedata 源的观察者

那么,我哪里做错了?

还有如何在单个语句中编写 returnedData 的代码,而不是在我正在执行的 2 个语句中编写它(一个用于调用 api,另一个用于覆盖订阅方法)。 p>

【问题讨论】:

【参考方案1】:

对于类似的情况,我会保留一堆一次性用品,并在开始新订阅时处理最旧的订阅。我在其中运行 OkHtttp 请求,所以在处理时,它们被中断了。

【讨论】:

【参考方案2】:

当用户创建另一个请求而前一个请求仍在进行中时,要取消以前的改造请求,您可以使用 kotlin 的协程来实现这一点。

您可以使用调用您的 api 请求的挂起函数启动协程,并保留在启动协程时返回的“作业”对象的引用以及当您的用户在前一个请求正在进行时发送另一个搜索请求时,您可以只需使用“job.isActive”检查之前的改造请求是否正在进行中,如果它处于活动状态,那么您可以调用“job.cancel()”来停止观察之前的 liveData 并开始一个新的 Job。

class MainViewModel @Inject constructor() : ViewModel() 
// Job instance
private var job = Job()

fun requestApiForResponse(val searchQuery:String)
 if(job?.isActive) // check if any previous job is running , if true then cancel that 
   job?.cancel()
 job = GlobalScope.launch(Dispatchers.IO)
   //call your retrofit Request here
 


override fun onCleared() 
        super.onCleared()
        job.cancel()
    

查看这个github的仓库search functionality with liveData and coroutines

【讨论】:

以上是关于如何处理以前的请求并停止观察以前的 LiveData的主要内容,如果未能解决你的问题,请参考以下文章

如何处理SCD 2类维和重复维记录?

大牛教你如何处理好前后端分离的 API 问题

前后端分离开发,Vue 如何处理跨域问题?

selenium 如何处理table

注销后谷歌如何处理后退按钮?

调用 REST API 时如何处理 Google Ads API 速率限制?