LiveData哭着对我说,你不要用Flow代替我,没有LiveData如何实现轻松MVVM

4,997 阅读3分钟

     本文说明如何使用flow来代替LiveData实现mvvm

      免责声明:flow 和 channel处于实验阶段,后续api会可能变化比较大。

     使用协程过程中发现,Kotlin的flow channel 还在试验阶段,所以一直没用上。但是因为LiveData的设计是不好处理Backpressure的,熟悉Rx的同学,知道Rx的同学知道,Rx官方文档给我们展示了如何处理背压。但是LiveData真的有时候不好用,但是要实现mvvm,我们却不得不用它。

      但是其实我们完全可以使用flow来代替它。而且使用了之后会发现:真。。真 香!!!


      下面我通过实现一个搜索功能,不使用LiveData只使用flow  来实现mvvm。LiveData,再见~~

      首先上才艺:(github链接 :github.com/JJJsn/struc…

   

直接上代码

@ExperimentalCoroutinesApi
@FlowPreview
class SearchBarActivity  :AppCompatActivity(){  
    lateinit var adapter: SearchAdapter    //展示搜索结果的recyclerview的适配器
    val viewModel by viewModels<SearchBarViewModel>(      
    SearchBarViewModelFactory() )   //使用 activity-ktx包里面的扩展函数减少模板代码

    override fun onCreate(savedInstanceState: Bundle?) {    
        super.onCreate(savedInstanceState)      
        setContentView(R.layout.activity_search)       
        rv_search_result.adapter=SearchAdapter().also { adapter=it } //设置recyclerview 
        rv_search_result.layoutManager=LinearLayoutManager(this)     
        et_keyWord.doAfterTextChanged { keyWord->     
            with(viewModel){               
                channel.offer(keyWord.toString())    //给channel发送数据
            }       
        }           
     lifecycleScope.launchWhenStarted {      
         //当我们的activity处于活跃状态的时候 ,才会collect我们的flow。
         //实现和LiveData一摸一样的效果
         viewModel.searchResultFlow.collect { searchResult ->       
             when(searchResult){          
                 is Result.Loading -> {}           
                 is Result.Error-> { showMessage(searchResult.toString())}    
                 is Result.Success -> {adapter.submitList(searchResult.data) }   
            }          
         }      
     }   
   }
}

然后是我们的viewModel:

@ExperimentalCoroutinesApi
class SearchBarViewModel :ViewModel() {   
     //自动处理 backpressure ,真是香    
    val channel =ConflatedBroadcastChannel<String>() //channel 的缓存大小为 1  
 
    @FlowPreview   
    val searchResultFlow: Flow<Result<List<String>>> =channel.asFlow()   
       .map { key ->     
           delay(200)        
           Result.Success(repositoty.filter {     
               it.contains(key)   
           }) as Result<List<String>>
        }    
       .onStart {}       
       .flowOn(Dispatchers.IO)  //这行代码上面的代码都会在 IO线程执行。
       .catch { e:Throwable ->   
           emit(Result.Error(e as Exception))    
        }
   
    override fun onCleared() {    
        super.onCleared()     
        channel.close()
    }  
  val repositoty= mutableListOf<String>().apply {        //测试数据  
      add("123我爱你");add("17岁");add("123木头人")    
      add("11111");add("1234");add("1990");add("1111");add("111");add("119")     
      add("1121"); add("112");add("1111")      
      add("111 Summer Classics");add("111111");add("111 (Centoundici)");
      add("11:11 (Amended)");    
      add("11111101");add("11112")    
      add("1111111");add("11111101");
      add("11111 (feat. Jake Candieux, Dan Monic..)");add("11111111")   
  }
}

欧克,我们没有使用LiveData,实现了一摸一样的效果!!

这里重点说下ConflatedBroadcastChannel

我也不太能说清楚,直接上图上链接:

 图片来自文章 proandroiddev.com/kotlin-coro…

channel是啥?:


                                  图1:channel的介绍



     channel的缓存类型,本文用的是Conflated:


                                图2:channel的缓存类型



参考文献:

1. Jag Saund   Dive into Croutines and Channels 

proandroiddev.com/kotlin-coro…

2.Süleyman Fatih Giriş    Use LiveData & Flow in MVVM 

proandroiddev.com/kotlin-coro…

3. Android_开发者 协程 Flow的最佳实践 

juejin.cn/post/684490…

4. 如何使用Flow和channel的sample,Sean McQuillan 

fragmentedpodcast.com/episodes/18…