白玩 一 記錄retrofit+okhttp+flow 及 kts的全局配置

先回憶下flow吧!

flow是啥

Flow 是 Kotlin 協程框架中的一個異步數據流處理組件,專為響應式編程設計,適用于需要連續或異步返回多個值的場景,如網絡請求、數據庫查詢、傳感器數據等

  • 1 ?異步流(Asynchronous Streams)
    • Flow 允許以非阻塞方式處理一系列值或事件,適用于大量數據或涉及 IO 操作的場景
    • 與 suspend 函數不同,Flow 可以返回多個值,而 suspend 函數僅能返回單個計算結果
  • 2 冷流(Cold Flow)與熱流(Hot Flow)
    • 冷流?:僅在收集器(collect)訂閱后才會開始發射數據
      ? - 熱流?(如 SharedFlow):創建后立即發射數據,無論是否有收集器訂閱
  • 3 聲明式 API?
    • 提供豐富的操作符(如 map、filter、reduce),支持鏈式調用
  • 4 協程集成?
    • Flow 基于協程,支持結構化并發、取消操作和背壓管理

flow的創建

  • 使用 flow{} 構建器
val numberFlow = flow {emit(1)delay(100) // 模擬耗時操作emit(2)
}
  • 使用 flowOf 快速創建
val fixedFlow = flowOf(1, 2, 3)
  • 集合轉 Flow
val listFlow = listOf("A", "B", "C").asFlow()
高級創建方式

(1) 基于回調的 API 轉換
將回調式 API(如網絡請求)封裝為 Flow?

fun fetchDataFlow() = callbackFlow {val callback = object : DataCallback {override fun onData(value: String) {trySend(value) // 發送數據到 Flow}override fun onComplete() {close() // 關閉 Flow}}registerCallback(callback)awaitClose { unregisterCallback(callback) } // 確保資源釋放
}

(2) 從掛起函數生成
通過 channelFlow 或 flow 結合掛起函數實現復雜邏輯?

fun pollUpdates() = flow {while (true) {val updates = fetchUpdates() // 掛起函數emit(updates)delay(5000) // 間隔輪詢}
}

Flow 中處理背壓(Backpressure)的核心策略

  1. 緩沖機制?
    通過 buffer() 設置緩沖區容量,允許生產者和消費者異步執行,緩解數據積壓壓力?
flow { repeat(100) { emit(it) }
}.buffer(50) // 設置50容量的緩沖區.collect { /* 處理數據 */ }

?2. 合并策略
使用 conflate() 跳過中間值,僅保留最新數據?

flow { emit(1); delay(10); emit(2); emit(3) 
}.conflate().collect { println(it) } // 輸出:1 → 3(跳過2)

?3. 最新值優先
collectLatest 取消未完成的任務,立即處理最新發射值?

flow { emit("A"); delay(100); emit("B") 
}.collectLatest { value ->delay(200)  // 處理"A"時被"B"中斷println(value) // 僅輸出"B"
}
  1. 調度優化
    利用 flowOn 切換協程上下文,分散計算負載?
flow { /* 密集計算 */ }.flowOn(Dispatchers.Default) // 在后臺線程生產.collect { /* 主線程消費 */ }

創建操作符?

flow{}?
基礎構建器,通過 emit 發射數據,支持掛起操作?:

flow { emit(1); delay(100); emit(2) }

flowOf?
快速創建固定值序列的 Flow?:

flowOf("A", "B", "C")

asFlow?
將集合(如 List)轉換為 Flow?:

listOf(1, 2, 3).asFlow()

轉換操作符?

map?
對每個元素進行轉換?:

flowOf(1, 2).map { it * 10 } // 輸出 10, 20

filter?
按條件過濾元素?:

flowOf(1, 2, 3).filter { it % 2 == 0 } // 輸出 2

transform?
靈活轉換,可多次發射值?:

flowOf("Hi").transform { emit(it.uppercase()); emit(it.length) }

組合操作符?

zip?
合并兩個 Flow 的對應元素?:

flowOf(1, 2).zip(flowOf("A", "B")) { num, str -> "$num$str" } // 輸出 1A, 2B

flatMapConcat?
順序展開嵌套 Flow?:

flowOf(1, 2).flatMapConcat { flowOf(it, it * 2) } // 輸出 1, 2, 2, 4

終端操作符?

collect?
觸發流執行并處理數據?:

flowOf(1).collect { println(it) }

first/last?
獲取首個或末尾元素?:

flowOf(1, 2).first() // 返回 1

reduce?
累積計算(如求和)?:

flowOf(1, 2, 3).reduce { acc, v -> acc + v } // 輸出 6

背壓處理操作符?

buffer?
設置緩沖區緩解生產消費速度差異?:

flow { emit(1) }.buffer(10)

conflate?
跳過中間值,保留最新數據?:

flow { emit(1); emit(2) }.conflate() // 僅處理 2

其他實用操作符?

take?
限制收集的元素數量?:

flowOf(1, 2, 3).take(2) // 輸出 1, 2

onEach?
在每次發射時執行副作用(如日志)?:

flowOf(1).onEach { println("發射: $it") }

開整

調用retrofit+okhttp

導包

// 網絡請求api("com.google.code.gson:gson:2.8.6")api("com.squareup.retrofit2:retrofit:2.9.0")api("com.squareup.retrofit2:converter-gson:2.9.0")api("com.squareup.retrofit2:converter-scalars:2.0.0")api("com.squareup.okhttp3:okhttp:3.14.9")api("com.squareup.okhttp3:logging-interceptor:3.12.2")api("com.squareup.okio:okio:1.17.4")

open class HttpCreater private constructor() {val timeOut: Long = 60 //30秒超時val baseUrlInterceptor: BaseUrlInterceptor = BaseUrlInterceptor()var okhttpClient: OkHttpClientvar downOkHttpClient: OkHttpClient//日志攔截var loggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {override fun log(message: String) {//打印retrofit日志Log.i("log_http", "retrofitBack = $message")}})init {loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY//用于請求okhttpClient = OkHttpClient.Builder().connectTimeout(timeOut, TimeUnit.SECONDS).readTimeout(timeOut, TimeUnit.SECONDS).writeTimeout(timeOut, TimeUnit.SECONDS).addInterceptor(baseUrlInterceptor).addInterceptor(loggingInterceptor).build()//用于下載  因為請求的okhttpClient 有日志攔截器 會先攔截請求結果 所以專門創建個用于下載的downOkHttpClient = OkHttpClient.Builder().connectTimeout(timeOut, TimeUnit.SECONDS).readTimeout(timeOut, TimeUnit.SECONDS).writeTimeout(timeOut, TimeUnit.SECONDS).build()}companion object {open val instance by lazy (LazyThreadSafetyMode.SYNCHRONIZED){HttpCreater()}}/*** 設置baseUrl 對應的token*/open fun setToken(baseUrl: String,token:String){baseUrlInterceptor.setToken(baseUrl,token)}/*** 獲取請求的retrofit  調用的時候創建 傳入baseUrl  因為我們項目連了好幾個服務器*/open fun getRetrofit(baseUrl:String) : Retrofit{val gson: Gson = GsonBuilder().setLenient().create();return  Retrofit.Builder().client(okhttpClient).baseUrl(baseUrl)
//            .addConverterFactory(GsonConverterFactory.create(gson)) //配置轉化庫 Gson解析失敗,不報錯崩潰.addConverterFactory(ScalarsConverterFactory.create()) //返回字符串.build()}/*** 下載的tokenretrofit*/open fun getDownRetrofit(baseUrl:String) : Retrofit{return  Retrofit.Builder().client(downOkHttpClient).baseUrl(baseUrl).addConverterFactory(ScalarsConverterFactory.create()) //配置轉化庫 Gson解析失敗,不報錯崩潰.build()}
}

請求攔截器 用于獲取token后設置后,自動將token設置到請求頭

class BaseUrlInterceptor:Interceptor {val tokenMap = mutableMapOf<String,String>()override fun intercept(chain: Interceptor.Chain): Response {// 獲取requestval request = chain.request()val builder = request.newBuilder()builder.addHeader("Content-Type", "application/json; charset=UTF-8")builder.addHeader("Accept", "application/json;versions=1")val httpUrl = request.url().url().hostLog.i("log_http","httpUrl>>${httpUrl}")if(!tokenMap.get(httpUrl).isNullOrEmpty()){builder.addHeader("Authorization", "Bearer ${tokenMap.get(httpUrl)}")}return chain.proceed(builder.build())}fun setToken(baseUrl: String, token: String) {tokenMap.put(baseUrl,token)}
}

添加請求接口

interface NetApi {@GET("/article/list/{path}/json")suspend fun getList(@Path("path") page:Int):String}

NetRepository flow調用

class NetRepository private constructor(){val service by lazy { HttpCreater.instance.getRetrofit("https://www.wanandroid.com").create(NetApi::class.java) }companion object{val instance by lazy { NetRepository() }}fun getList():Flow<String> = flow {val result = service.getList(0)Log.i("zq_demo","flow  result>>${result}")emit(result)}
}

測試

測試代碼

findViewById<TextView>(R.id.tv_hello).setOnClickListener {Log.i("zqq_demo","tv_hello")lifecycleScope.launch {Log.i("zqq_demo","lifecycleScope")netRepository.getList().onEach {Log.i("zqq_demo","onEach>>${it}")}.collect{Log.i("zqq_demo","collect>>${it}")}}}

結果
在這里插入圖片描述

其他

1 我們可以使用配置文件配置baseurl
創建config.gradle.kts
在這里插入圖片描述
添加測試代碼

val myProperty: String by extra("Hello, World!")

使用 app下
在這里插入圖片描述
添加

// 加載 config.gradle.kts
apply(from = "${rootDir}/config.gradle.kts")
// 使用 myProperty
println(extra["myProperty"]) // 輸出: Hello, World!

運行
在這里插入圖片描述
接下來創建baseUrl
config.gradle.kts:

val baseUrl: String by extra("https://www.wanandroid.com")

app下 build.gradle.kts

plugins {alias(libs.plugins.android.application)alias(libs.plugins.jetbrains.kotlin.android)
}
// 加載 config.gradle.kts
apply(from = "${rootDir}/config.gradle.kts")
android {defaultConfig {buildConfigField("String",name = "baseUrl", value = "\"${extra["baseUrl"]}\"")}//這個別忘記添加buildFeatures {buildConfig = true // 啟用 BuildConfig 功能}
}

然后打印

Log.i("zqq_demo","baseUrl>>${BuildConfig.baseUrl}")

在這里插入圖片描述
這樣上邊NetRepository 就可以使用

class NetRepository private constructor(){val service by lazy { HttpCreater.instance.getRetrofit(BuildConfig.baseUrl).create(NetApi::class.java) }

當然你可以定義版本號 applicationId 其他的key versionCode versionName 簽名文件路徑 等等配置都可以

使用舊的config.gradle

config.gradle如下

ext {test = "hello"android = [hello = "hello"]
}

項目的build.gradle.kts

plugins {alias(libs.plugins.android.application) apply falsealias(libs.plugins.jetbrains.kotlin.android) apply falsealias(libs.plugins.android.library) apply false
}apply(from = "${rootDir}/config.gradle")

app的build.gradle.kts

println("${(rootProject.extra["android"] as Map<String,Any>).get("hello")}")  defaultConfig {applicationId = "com.zqq.demo"minSdk = 24targetSdk = 34versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"buildConfigField("String",name = "hello", value = "\"${rootProject.extra["test"]}\"")}buildFeatures {buildConfig = true // 啟用 BuildConfig 功能}

可以去看 式封裝:Kotlin+協程+Flow+Retrofit+OkHttp +Repository,傾囊相授,徹底減少模版代碼進階之路

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/pingmian/91225.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91225.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91225.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

犯罪現場三維還原:科技助力刑偵變革

在刑偵領域&#xff0c;犯罪現場的準確還原對于案件偵破起著至關重要的作用。傳統的現場記錄方式&#xff0c;如拍照、繪圖等&#xff0c;雖然能獲取一定信息&#xff0c;但難以全面、直觀地呈現現場全貌&#xff0c;容易遺漏關鍵細節&#xff0c;且在后期分析和信息傳達上存在…

go-admin 構建arm鏡像

目錄 1、 go-admin Dockerfile 2、docker build go-admin 3、settings.yml 4、go-admin-ui Dockerfile 5、docker build go-admin-ui 6、go-admin.yaml 7、go-admin-ui.yaml 1、 go-admin Dockerfile # 構建階段:使用 Go 1.24 版本(支持遠程調試) FROM golang:1.24-…

深入淺出:C++ STL簡介與學習指南

目錄 前言 STL的版本演變 STL六大組件 STL的重要性 如何學習STL STL的缺陷 總結 前言 什么是STL&#xff1f; STL&#xff08;Standard Template Library&#xff0c;標準模板庫&#xff09;是C標準庫的核心組成部分&#xff0c;它不僅是一個可復用的組件庫&#xff0c;更是一…

Mysql事務原理

臟讀(Dirty Read) 某個事務已更新一份數據&#xff0c;另一個事務在此時讀取了同一份數據&#xff0c;由于某些原因&#xff0c;前一個進行了RollBack&#xff0c;則后一個事務所讀取的數據就會是不正確的。 不可重復讀(Non-repeatable read) 在一個事務的兩次查詢之中數據不一…

小紅書筆記詳情API指南

一、引言小紅書作為中國領先的社交電商平臺&#xff0c;擁有超過4.8億用戶(2025年Q2數據)&#xff0c;其開放平臺已成為品牌營銷與數據挖掘的重要渠道?1。通過筆記詳情API獲取數據&#xff0c;可以幫助商家、品牌方和數據分析人員了解用戶反饋、市場趨勢和消費需求?。這些數據…

VS+Qt中使用QCustomPlot繪制曲線標簽(附源碼)

在qt中我們常常會使用數據來繪制曲線&#xff0c;常用的的繪制方法用QCutomPlot、QChart和QPrinter。有時我們會根據需要在曲線進行二次繪制&#xff0c;包括對曲線打標簽&#xff0c;顯示某個點的值等功能。本文主要為大家介紹在QCustomPlot中使用QCPItemTracer和QCPItemText繪…

Spring Boot項目生產環境部署完整指南

在Spring Boot應用開發完成后&#xff0c;如何將其穩定、高效地部署到生產環境是每個開發者都需要掌握的關鍵技能。本文將詳細介紹Spring Boot項目的多種部署方案&#xff0c;從傳統部署到現代化容器部署&#xff0c;選擇最適合的部署策略。 1. 部署前的準備工作 1.1 項目打包優…

微信小程序中實現頁面跳轉的方法

微信小程序中頁面跳轉主要有兩種方式&#xff1a;聲明式導航&#xff08;通過組件實現&#xff09;和編程式導航&#xff08;通過API實現&#xff09;。兩種方式適用于不同場景&#xff0c;以下詳細說明。一、聲明式導航&#xff08;navigator組件&#xff09;通過小程序內置的…

從0開始學linux韋東山教程Linux驅動入門實驗班(7)

本人從0開始學習linux&#xff0c;使用的是韋東山的教程&#xff0c;在跟著課程學習的情況下的所遇到的問題的總結,理論雖枯燥但是是基礎。本人將前幾章的內容大致學完之后&#xff0c;考慮到后續驅動方面得更多的開始實操&#xff0c;后續的內容將以韋東山教程Linux驅動入門實…

國內AI IDE競逐:騰訊CodeBuddy、阿里通義靈碼、字節跳動TRAE、百度文心快碼

國內AI IDE競逐&#xff1a;騰訊CodeBuddy、阿里通義靈碼、字節跳動TRAE、百度文心快碼 隨著人工智能技術的不斷發展&#xff0c;各大科技公司紛紛推出自家的AI IDE&#xff0c;推動軟件開發進入全新的智能化時代。騰訊的 CodeBuddy IDE、阿里云的 通義靈碼 AI IDE、字節跳動的…

git rebase使用教程 以及和merge的區別

Merge和Rebase概念概述 rebase 和 merge 相似&#xff0c;但又不完全相同&#xff0c;本質上都是用來合并分支的命令&#xff0c;區別如下 merge合并分支會多出一條merge commit記錄&#xff0c;而rebase不會merge的提交樹是非線性的&#xff0c;會有分叉&#xff0c;而rebase的…

React中的合成事件解釋和理解

什么是合成事件&#xff08;Synthetic event&#xff09;?它和原生事件有什么區別?解題思路:解釋合成事件&#xff0c;然后對比原生事件&#xff0c;然后再說他的優勢1.一致性 在 react里面&#xff0c;這個合成事件是非常重要的&#xff0c;因為它就是為了解決瀏覽器之間與事…

【Python系列】使用 memory_profiler 診斷 Flask 應用內存問題

博客目錄一、內存分析的重要性二、memory_profiler 基礎使用安裝與基本配置理解分析報告三、在 Flask 應用中使用 memory_profiler裝飾視圖函數使用 mprof 進行長期監控四、高級內存分析技巧精確測量代碼塊內存定期內存采樣結合 objgraph 分析對象引用五、常見內存問題及解決方…

vue3【組件封裝】超級表單 S-form.vue

最終效果 代碼實現 components/SUI/S-form.vue <script lang"ts" setup> import type { FormInstance } from "element-plus";// 使用索引簽名定義對象類型 type GenericObject {[key: string]: any; };const props defineProps<{Model?: Gen…

Android Studio Memory Monitor內存分析核心指標詳解

Depth、Native Size、Shallow Size、Retained Size 解析 一、指標定義與對比指標定義計算邏輯重要性Shallow Size對象自身實例占用的內存基本類型字段大小 引用指針 內存對齊對象的基礎內存成本Retained Size回收該對象可釋放的總內存量&#xff08;含所有依賴對象&#xff0…

vue中使用wavesurfer.js繪制波形圖和頻譜圖(支持.pcm)

新的實現方式&#xff1a;vue使用Canvas繪制頻譜圖 安裝wavesurfer.js npm install wavesurfer.js第一版&#xff1a; 組件特點&#xff1a; 一次性加載好所有的數據&#xff1b; <template><div class"audio-visualizer-container"><div class&…

go mod教程、go module

什么是go mod go mod 是go語言的包管理工具&#xff0c;類似java 的maven&#xff0c;go mod的出現可以告別goPath&#xff0c;使用go module來管理項目&#xff0c;有了go mod賬號就不需要非得把項目放到gopath/src目錄下了&#xff0c;你可以在磁盤的任何位置新建一個項目 go…

150-SWT-MCNN-BiGRU-Attention分類預測模型等!

150-SWT-MCNN-BiGRU-Attention分類預測模型!基于多尺度卷積神經網絡(MCNN)雙向長短期記憶網絡(BiGRU)注意力機制(Attention)的分類預測模型&#xff0c;matlab代碼&#xff0c;直接運行使用&#xff01;1、模型介紹&#xff1a;針對傳統方法在噪聲環境下診斷精度低的問題&#…

MySQL數據一致性與主從延遲深度解析:從內核機制到生產實踐

在高并發分布式系統中&#xff0c;數據一致性與復制延遲如同硬幣的兩面。本文深入剖析MySQL持久化機制與主從同步原理&#xff0c;并提供可落地的調優方案。一、數據持久化核心機制&#xff1a;雙日志協同 1. Redo Log&#xff1a;崩潰恢復的生命線刷新策略&#xff08;innodb_…

【I】題目解析

目錄 單選題 多選題 判斷題 單選題 1.reg[7:0]A; A2hFF;則A&#xff08;&#xff09; A.8b11111110 B.8b03 C.8b00000011 D.8b11111111 C 2hFF實際上等效于2位二進制2b11&#xff0c;賦值給8位寄存器A之后&#xff0c;低位賦值&#xff0c;高位補0 A8b00000011 AMD FPG…