• Android Kotlin Retrofit 与Flow。两个Flow用LiveData来进行分解


    这里用到了网络。。用的php构建的假数据

     核心代码如下

    1. // ---------------------------引入接口参数类(以用户实际路径为准)---------------------------------
    2. namespace App\Services\test;
    3. use App\Common\Services\BaseService;
    4. use App\Http\Controllers\ExpressController;
    5. use App\Services\express\ExpressService;
    6. use App\Services\utils\IpServices;
    7. class TestService extends BaseService
    8. {
    9. public function index($key): \Illuminate\Http\JsonResponse
    10. {
    11. $data = array();
    12. $dataSub = array();
    13. $dataSub['text'] = $key . "乱七八糟噢噢噢噢的数据";
    14. $dataSub['id'] = 1;
    15. $data[] = $dataSub;
    16. $dataSub['text'] = $key . "你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲";
    17. $dataSub['id'] = 2;
    18. $data[] = $dataSub;
    19. $dataSub['text'] = $key . "去吗,配吗 这褴褛的披风";
    20. $dataSub['id'] = 3;
    21. $data[] = $dataSub;
    22. return $this->apiSuccess("1", $data);
    23. }
    24. }

    最后返回json结果如下

    1. {
    2. "code": 20000,
    3. "message": "1",
    4. "data": [{
    5. "text": "123乱七八糟噢噢噢噢的数据",
    6. "id": 0
    7. }, {
    8. "text": "123你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲",
    9. "id": 1
    10. }, {
    11. "text": "123去吗,配吗 这褴褛的披风",
    12. "id": 1
    13. }]
    14. }

    好 正题开始咯

     

    1. package com.example.android_flow_practice.net
    2. import com.example.android_flow_practice.model.Article
    3. import com.example.android_flow_practice.model.NetResponse
    4. import retrofit2.http.GET
    5. import retrofit2.http.Query
    6. interface ArticleApi {
    7. @GET("api/v1/open/test")
    8. suspend fun searchArticles(
    9. @Query("key") key: String
    10. ): NetResponse>
    11. }

     

    1. package com.example.android_flow_practice.net
    2. import okhttp3.OkHttpClient
    3. import retrofit2.Retrofit
    4. import retrofit2.converter.gson.GsonConverterFactory
    5. import retrofit2.create
    6. object RetrofitClient {
    7. val url = "https://xxx.xxx.com/";
    8. private val instance: Retrofit by lazy {
    9. Retrofit.Builder().client(OkHttpClient.Builder().build()).baseUrl(url)
    10. .addConverterFactory(GsonConverterFactory.create()).build()
    11. }
    12. val articleApi: ArticleApi by lazy {
    13. instance.create(ArticleApi::class.java)
    14. }
    15. }

    依然是马赛克的url。。多余的靠自己

    准备adapter

    1. package com.example.android_flow_practice.adapter
    2. import android.content.Context
    3. import android.view.LayoutInflater
    4. import android.view.ViewGroup
    5. import androidx.recyclerview.widget.RecyclerView
    6. import com.example.android_flow_practice.databinding.ItemArticleBinding
    7. import com.example.android_flow_practice.databinding.ItemUserBinding
    8. import com.example.android_flow_practice.db.User
    9. import com.example.android_flow_practice.model.Article
    10. class ArticleAdapter(private val context: Context) : RecyclerView.Adapter() {
    11. private val data = ArrayList
      ()
    12. fun setData(data: List<Article>) {
    13. this.data.clear()
    14. this.data.addAll(data);
    15. notifyDataSetChanged()
    16. }
    17. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
    18. val binding = ItemArticleBinding.inflate(LayoutInflater.from(context), parent, false)
    19. return BindingViewHolder(binding = binding)
    20. }
    21. override fun getItemCount(): Int {
    22. return data.size
    23. }
    24. override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
    25. val item = data[position]
    26. val binding = holder.binding as ItemArticleBinding
    27. binding.text.text = "${item.id}, ${item.text}"
    28. }
    29. }

    对应的viewHolder

    1. package com.example.android_flow_practice.adapter
    2. import androidx.recyclerview.widget.RecyclerView
    3. import androidx.viewbinding.ViewBinding
    4. class BindingViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {}

    布局

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="wrap_content"
    5. android:orientation="vertical">
    6. <androidx.appcompat.widget.AppCompatTextView
    7. android:id="@+id/text"
    8. android:layout_width="match_parent"
    9. android:layout_height="wrap_content"
    10. android:layout_gravity="center"
    11. android:gravity="center"
    12. android:paddingVertical="4dp"
    13. android:textSize="26sp" />
    14. </LinearLayout>

    然后adapter是在Fragment当中的 

    Fragment布局

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:tools="http://schemas.android.com/tools"
    4. android:layout_width="match_parent"
    5. android:layout_height="match_parent"
    6. xmlns:app="http://schemas.android.com/apk/res-auto"
    7. android:orientation="vertical"
    8. tools:context=".fragment.UserFragment">
    9. <androidx.appcompat.widget.AppCompatEditText
    10. android:id="@+id/ed_search"
    11. android:layout_width="match_parent"
    12. android:layout_height="wrap_content"
    13. android:hint="Input keyword for search"
    14. android:padding="8.dp"
    15. />
    16. <androidx.recyclerview.widget.RecyclerView
    17. android:id="@+id/rv"
    18. app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    19. android:layout_width="match_parent"
    20. android:layout_height="match_parent" />
    21. </LinearLayout>

     布局完事以后代码

    1. package com.example.android_flow_practice.fragment
    2. import android.os.Bundle
    3. import android.text.Editable
    4. import android.text.TextWatcher
    5. import android.util.Log
    6. import androidx.fragment.app.Fragment
    7. import android.view.LayoutInflater
    8. import android.view.View
    9. import android.view.ViewGroup
    10. import android.widget.TextView
    11. import androidx.fragment.app.viewModels
    12. import androidx.lifecycle.lifecycleScope
    13. import com.example.android_flow_practice.R
    14. import com.example.android_flow_practice.adapter.ArticleAdapter
    15. import com.example.android_flow_practice.databinding.FragmentArticleBinding
    16. import com.example.android_flow_practice.databinding.FragmentDownloadBinding
    17. import com.example.android_flow_practice.viewmodel.ArticleViewModel
    18. import com.example.android_flow_practice.viewmodel.UserViewModel
    19. import kotlinx.coroutines.channels.awaitClose
    20. import kotlinx.coroutines.flow.Flow
    21. import kotlinx.coroutines.flow.callbackFlow
    22. import kotlinx.coroutines.flow.collect
    23. class ArticleFragment : Fragment() {
    24. private val TAG = "ArticleFragment"
    25. private val viewModel: ArticleViewModel by viewModels()
    26. private val mBinding: FragmentArticleBinding by lazy {
    27. FragmentArticleBinding.inflate(layoutInflater)
    28. }
    29. override fun onCreateView(
    30. inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    31. ): View? {
    32. // Inflate the layout for this fragment
    33. return mBinding.root
    34. }
    35. override fun onActivityCreated(savedInstanceState: Bundle?) {
    36. super.onActivityCreated(savedInstanceState)
    37. lifecycleScope.launchWhenCreated {
    38. mBinding.edSearch.textWatcherFlow().collect {
    39. Log.e(TAG, "onActivityCreated: ${it}")
    40. viewModel.searchArticles(it)
    41. }
    42. }
    43. context?.let {
    44. val adapter = ArticleAdapter(it)
    45. mBinding.rv.adapter = adapter
    46. viewModel.articles.observe(viewLifecycleOwner, { artices ->
    47. adapter.setData(artices)
    48. })
    49. }
    50. }
    51. //获取关键字
    52. fun TextView.textWatcherFlow(): Flow = callbackFlow {
    53. val textWatcher = object : TextWatcher {
    54. override fun beforeTextChanged(
    55. s: CharSequence?, start: Int, count: Int, after: Int
    56. ) {
    57. }
    58. override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    59. }
    60. override fun afterTextChanged(s: Editable?) {
    61. trySend(s.toString()).isSuccess
    62. }
    63. }
    64. addTextChangedListener(textWatcher)
    65. awaitClose { removeTextChangedListener(textWatcher) }
    66. }
    67. }

    核心代码存放与viewModel当中

    1. package com.example.android_flow_practice.viewmodel
    2. import android.app.Application
    3. import androidx.lifecycle.AndroidViewModel
    4. import androidx.lifecycle.MutableLiveData
    5. import androidx.lifecycle.viewModelScope
    6. import com.example.android_flow_practice.model.Article
    7. import com.example.android_flow_practice.net.RetrofitClient
    8. import kotlinx.coroutines.Dispatchers
    9. import kotlinx.coroutines.flow.*
    10. import kotlinx.coroutines.launch
    11. import okhttp3.Dispatcher
    12. class ArticleViewModel(app: Application) : AndroidViewModel(app) {
    13. val articles = MutableLiveData<List<Article>>()
    14. fun searchArticles(key: String) {
    15. viewModelScope.launch {
    16. flow {
    17. val list = RetrofitClient.articleApi.searchArticles(key)
    18. emit(list)
    19. }.flowOn(Dispatchers.IO).catch { e -> e.printStackTrace() }.collect {
    20. it.data.let {
    21. articles.setValue(it)
    22. }
    23. }
    24. }
    25. }
    26. }

    这里简单拆析下这里

     使用flow 监听 

    afterTextChanged 将文本变动后的数据返回过来。

    又监听其关闭方法

    awaitClose 对textWather进行解除监听也就是释放
    

    这是其一

    其二 将viewModel的搜索列表数据弄到了articles

     然后再次对其进行监听 这样结构数据就不是collect{  collect{} }了

    那我们试下这种错误的写法看看会怎么样

    collect{  collect{} }

    viewModel中复制修改

    1. fun searchArticles2(key: String) = flow {
    2. val list = RetrofitClient.articleApi.searchArticles(key)
    3. emit(list)
    4. }.flowOn(Dispatchers.IO).catch { e -> e.printStackTrace() }

    fragment也有所改变

    1. override fun onActivityCreated(savedInstanceState: Bundle?) {
    2. super.onActivityCreated(savedInstanceState)
    3. lifecycleScope.launchWhenCreated {
    4. mBinding.edSearch.textWatcherFlow().collect {
    5. Log.e(TAG, "onActivityCreated: ${it}")
    6. viewModel.searchArticles2(it).collect{ dataList->
    7. context?.let {
    8. val adapter = ArticleAdapter(it)
    9. mBinding.rv.adapter = adapter
    10. adapter.setData(dataList.data)
    11. }
    12. }
    13. }
    14. }

    这样做依然没有问题

    但是并不推荐。 

    因为collect{  collect{} } 并不是流的设计思想。。

    collect{  }

    collect{} 这种事

    你可以明显的感觉到 使用上述不推荐的写法

    collect{  collect{} } 这种写法。速度完全跟不上

    1. collect{  }
    2. collect{}

  • 相关阅读:
    【系统性】 循序渐进学C++
    树、二叉树、堆及其应用(堆排序、top-k问题)
    GO语言gin框架实战-03-swagger和接口文档
    HBase RowKey 的设计原则?
    3D+AR技术的应用,让时尚行业玩出新花样!
    分布式.远程调用技术概述
    将博客搬至CSDN
    安全性归约(游戏)
    Linux静态IP、DNS配置(VM的三种网络模式)
    这个Spring Security登录插件牛啊,验证码、小程序、OAuth2都能快速接入
  • 原文地址:https://blog.csdn.net/mp624183768/article/details/126807181