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

这里用到了网络。。用的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
}]
}

好 正题开始咯

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

 

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>

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

 布局完事以后代码

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)
}

}
}
}
}

这里简单拆析下这里

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

 使用flow 监听 


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


又监听其关闭方法


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


这是其一

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

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

 然后再次对其进行监听 这样结构数据就不是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)
}
}
}
}

这样做依然没有问题

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

但是并不推荐。 

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

collect{  }

collect{} 这种事

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

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

collect{  }

collect{}