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

Posted 安果移不动

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 

以上是关于Android Kotlin Retrofit 与Flow。两个Flow用LiveData来进行分解的主要内容,如果未能解决你的问题,请参考以下文章

Android - ViewModel、LiveData、Room 和 Retrofit 以及协程放在 kotlin 中

如何使用 Retrofit Android Kotlin 发布 [重复]

无法使用 Kotlin Retrofit 将数据从 Android 插入到 Mysql [重复]

Android Kotlin Retrofit + SimpleXMLConverter ElementList 解析不正确

android查询天气demo,基于mvp+kotlin+rxjava2+room+retrofit2

Android Kotlin Retrofit Post Request 输入的数据未发送