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

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

 核心代码如下

<?php
// ---------------------------引入接口参数类(以用户实际路径为准)---------------------------------
namespace App\Services\test;

use App\Common\Services\BaseService;
use App\Http\Controllers\ExpressController;
use App\Services\express\ExpressService;
use App\Services\utils\IpServices;

class TestService extends BaseService
{
    public function index($key): \Illuminate\Http\JsonResponse
    {

        $data = array();
        $dataSub = array();
        $dataSub['text'] = $key . "乱七八糟噢噢噢噢的数据";
        $dataSub['id'] = 1;
        $data[] = $dataSub;

        $dataSub['text'] = $key . "你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲";
        $dataSub['id'] = 2;
        $data[] = $dataSub;

        $dataSub['text'] = $key . "去吗,配吗 这褴褛的披风";
        $dataSub['id'] = 3;
        $data[] = $dataSub;


        return $this->apiSuccess("1", $data);

    }

}

最后返回json结果如下

{
	"code": 20000,
	"message": "1",
	"data": [{
		"text": "123乱七八糟噢噢噢噢的数据",
		"id": 0
	}, {
		"text": "123你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲",
		"id": 1
	}, {
		"text": "123去吗,配吗 这褴褛的披风",
		"id": 1
	}]
}

好 正题开始咯

package com.example.android_flow_practice.net

import com.example.android_flow_practice.model.Article
import com.example.android_flow_practice.model.NetResponse
import retrofit2.http.GET
import retrofit2.http.Query


interface ArticleApi {

    @GET("api/v1/open/test")
    suspend fun searchArticles(
        @Query("key") key: String
    ): NetResponse<List<Article>>
}
package com.example.android_flow_practice.net

import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create

object RetrofitClient {
    val url = "https://xxx.xxx.com/";
    private val instance: Retrofit by lazy {
        Retrofit.Builder().client(OkHttpClient.Builder().build()).baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create()).build()
    }


    val articleApi: ArticleApi by lazy {
        instance.create(ArticleApi::class.java)
    }
}

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

准备adapter

package com.example.android_flow_practice.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.android_flow_practice.databinding.ItemArticleBinding
import com.example.android_flow_practice.databinding.ItemUserBinding
import com.example.android_flow_practice.db.User
import com.example.android_flow_practice.model.Article

class ArticleAdapter(private val context: Context) : RecyclerView.Adapter<BindingViewHolder>() {

    private val data = ArrayList<Article>()
    fun setData(data: List<Article>) {
        this.data.clear()
        this.data.addAll(data);
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        val binding = ItemArticleBinding.inflate(LayoutInflater.from(context), parent, false)
        return BindingViewHolder(binding = binding)
    }

    override fun getItemCount(): Int {
        return data.size
    }

    override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
        val item = data[position]
        val binding = holder.binding as ItemArticleBinding
        binding.text.text = "${item.id}, ${item.text}"

    }
}

对应的viewHolder

package com.example.android_flow_practice.adapter

import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding


class BindingViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:paddingVertical="4dp"
        android:textSize="26sp" />

</LinearLayout>

然后adapter是在Fragment当中的 

Fragment布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".fragment.UserFragment">

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/ed_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Input keyword for search"
        android:padding="8.dp"

        />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</LinearLayout>

 布局完事以后代码

package com.example.android_flow_practice.fragment

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.example.android_flow_practice.R
import com.example.android_flow_practice.adapter.ArticleAdapter
import com.example.android_flow_practice.databinding.FragmentArticleBinding
import com.example.android_flow_practice.databinding.FragmentDownloadBinding
import com.example.android_flow_practice.viewmodel.ArticleViewModel
import com.example.android_flow_practice.viewmodel.UserViewModel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect


class ArticleFragment : Fragment() {
    private val TAG = "ArticleFragment"
    private val viewModel: ArticleViewModel by viewModels()

    private val mBinding: FragmentArticleBinding by lazy {
        FragmentArticleBinding.inflate(layoutInflater)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return mBinding.root
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        lifecycleScope.launchWhenCreated {
            mBinding.edSearch.textWatcherFlow().collect {
                Log.e(TAG, "onActivityCreated: ${it}")
                viewModel.searchArticles(it)
            }
        }
        context?.let {
            val adapter = ArticleAdapter(it)
            mBinding.rv.adapter = adapter
            viewModel.articles.observe(viewLifecycleOwner, { artices ->
                adapter.setData(artices)
            })
        }


    }

    //获取关键字

    fun TextView.textWatcherFlow(): Flow<String> = callbackFlow {
        val textWatcher = object : TextWatcher {
            override fun beforeTextChanged(
                s: CharSequence?, start: Int, count: Int, after: Int
            ) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                trySend(s.toString()).isSuccess

            }
        }
        addTextChangedListener(textWatcher)
        awaitClose { removeTextChangedListener(textWatcher) }
    }
}

核心代码存放与viewModel当中

package com.example.android_flow_practice.viewmodel

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.android_flow_practice.model.Article
import com.example.android_flow_practice.net.RetrofitClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import okhttp3.Dispatcher


class ArticleViewModel(app: Application) : AndroidViewModel(app) {
    val articles = MutableLiveData<List<Article>>()

    fun searchArticles(key: String) {
        viewModelScope.launch {
            flow {
                val list = RetrofitClient.articleApi.searchArticles(key)
                emit(list)
            }.flowOn(Dispatchers.IO).catch { e -> e.printStackTrace() }.collect {
                it.data.let {
                    articles.setValue(it)
                }

            }
        }
    }
}

这里简单拆析下这里

 使用flow 监听 

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

又监听其关闭方法

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

这是其一

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

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

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

collect{  collect{} }

viewModel中复制修改


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

fragment也有所改变

 override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        lifecycleScope.launchWhenCreated {
            mBinding.edSearch.textWatcherFlow().collect {
                Log.e(TAG, "onActivityCreated: ${it}")
                viewModel.searchArticles2(it).collect{ dataList->
                    context?.let {
                        val adapter = ArticleAdapter(it)
                        mBinding.rv.adapter = adapter
                        adapter.setData(dataList.data)
                    }
                }
            }
        }

这样做依然没有问题

但是并不推荐。 

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

collect{  }

collect{} 这种事

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

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

collect{  }

collect{} 

猜你喜欢

转载自blog.csdn.net/mp624183768/article/details/126807181