当前位置: 首页 > news >正文

Android 网络请求:EasyNet(Okhttp + retrofit + flow + gson + 缓存 + 文件下载 + 文件上传 + 人性化Loading窗)

Android 网络请求:EasyNet(Okhttp + retrofit + flow + gson + 缓存 + 文件下载 + 文件上传 + 人性化Loading窗)

该模块基于 me.laujiangtao.net 网络库封装,提供了完整的网络请求功能,包括基础API调用、文件上传下载、缓存机制等。

0. 接口准备(运行本地接口测试)

在当前目录下放置待下载的文件,文件名test.zip,命令行运行 api-server-0.0.1.jar

java -jar api-server-0.0.1.jar

将手机与电脑置于同一网络环境中,源码中更改ip地址为电脑ip

1. 模块结构

net/
├── api/                    # API接口定义
│   ├── ApiService.kt       # 基础API接口
│   ├── FileApiService.kt   # 文件操作相关API接口
│   └── RawApiService.kt    # 原始数据API接口
├── base/                   # 基础类
│   └── BaseResponse.kt     # 基础响应数据类
├── HttpConst.kt            # 网络常量
├── HttpEngine.kt           # 网络请求引擎(Repository)
└── ext.kt                  # 扩展函数

2. 初始化配置

引入依赖:

repositories {...maven(url = "https://gitee.com/laujiangtao/maven-repo/raw/main/")...
}dependencies {...implementation("me.laujiangtao.net:easynet:1.0.1")...
}

在 MyApplication 中初始化网络服务:

class MyApplication : Application() {override fun onCreate() {super.onCreate()// 注册API服务HttpServiceManager.addServiceFactory("apiService") {val config = HttpConfig(HttpConst.SERVER_URL, cacheDir = cacheDir)HttpService.create<ApiService>(config)}// 注册原始数据API服务HttpServiceManager.addServiceFactory("rawApiService") {val rawConfig = HttpConfig(HttpConst.SERVER_URL, cacheDir = cacheDir, useGsonConverter = false)HttpService.create<RawApiService>(rawConfig)}// 注册文件API服务HttpServiceManager.addServiceFactory("fileApiService") {val fileConfig = HttpConfig(HttpConst.SERVER_URL, isDebug = false)HttpService.create(fileConfig, FileApiService::class.java)}}
}

3. API接口定义

3.1 基础API (ApiService.kt)

定义了基本的网络请求接口:

interface ApiService {@GET("/index/helloworld")suspend fun helloWorld(): String@GET("/index/whois")@Cacheable(ttl = 30 * 1000, strategy = CacheStrategy.CACHE_FIRST)suspend fun whois(@Query("domain") domain: String): BaseResponse<Whois?>?@GET("/index/tianqi")@Cacheable(ttl = 30 * 1000, strategy = CacheStrategy.CACHE_FIRST)suspend fun weather(@Query("city") city: String): Tianqi
}

3.2 文件操作API (FileApiService.kt)

提供了文件上传下载相关接口:

interface FileApiService {@Multipart@POST("/file/uploadFiles")suspend fun upload(@Part parts: List<MultipartBody.Part>): String?@Streaming@GET("/file/download")suspend fun downloadFile(@Query("filename") fileUrl: String): ResponseBody// ... 其他文件操作接口
}

3.3 原始数据API (RawApiService.kt)

返回原始字符串数据的接口:

interface RawApiService {@GET("/index/meinv")@Cacheable(ttl = 30 * 1000, strategy = CacheStrategy.CACHE_FIRST)suspend fun pcmeinv(): String?
}

4. 网络请求引擎 (HttpEngine.kt)

HttpEngine 是网络请求的核心类,提供了两种风格的请求方法:

4.1 Flow风格请求

class HttpEngine {// Flow风格请求方法fun getHelloWorld(): Flow<RequestStatus<String?>> = apiCall {apiService?.helloWorld()}fun whois(domain: String): Flow<RequestStatus<BaseResponse<Whois?>?>> = apiCall {apiService?.whois(domain)}fun weather(city: String): Flow<RequestStatus<Tianqi?>> {return apiCall {apiService?.weather(city)}}// 文件上传下载fun uploadFile(request: FileUploadBean): Flow<RequestStatus<String?>> {return apiCall {fileApiService?.upload(request.toMultipartParts())}}fun downloadFile(fileUrl: String): Flow<RequestStatus<ResponseBody?>> {return apiCall {fileApiService?.downloadFile(fileUrl)}}
}

4.2 挂起函数风格请求

class HttpEngine {// 挂起函数风格请求方法suspend fun getWhoIsInfo(domain: String): BaseResponse<Whois?>? {return apiService?.whois(domain)}suspend fun getTianqiInfo(city: String): Tianqi? {return apiService?.weather(city)}suspend fun getPcmeinvInfo(): String? {return rawApiService?.pcmeinv()}
}

5. ViewModel层使用

在 MyViewModel 中使用 HttpEngine:

class MyViewModel : ViewModel() {private val repository = HttpEngine()// Flow风格fun getWhoIs(domain: String, cb: ((resp: RequestStatus<BaseResponse<Whois?>?>?) -> Unit)? = null) {viewModelScope.launch(CoroutineExceptionHandler { _, _ ->cb?.invoke(null)}) {repository.whois(domain).collect { result ->cb?.invoke(result)}}}// 挂起函数风格suspend fun getWhoIsSuspend(domain: String): BaseResponse<Whois?>? {return try {withContext(Dispatchers.IO) {repository.getWhoIsInfo(domain)}} catch (e: Exception) {Log.e("UserViewModel", "获取WhoIs信息失败", e)null}}
}

6. UI层调用示例

6.1 Flow风格调用 (FlowRequestActivity.kt)

class FlowRequestActivity : MyBaseActivity<ActivityFlowRequestBinding>() {private fun setOnClick() {binding.flowRequestBaseResponse.setOnClickListener {viewModel.getWhoIs("xxhzm.cn") { result ->result?.handleBaseResponse(onDone = {showData(it.toString())},onException = {showError("异常: ${it.message}")},onStart = {showProgress()},onEnd = {hideProgress()})}}}
}

只关注成功状态

class FlowRequestActivity : MyBaseActivity<ActivityFlowRequestBinding>() {private fun setOnClick() {binding.flowRequestBaseResponse.setOnClickListener {viewModel.getWhoIs("xxhzm.cn") { result ->result?.handleBaseResponse( onDone = {showData(it.toString())})}}}
}

6.2 挂起函数风格调用 (SuspendRequestActivity.kt)

class SuspendRequestActivity : MyBaseActivity<ActivitySuspendRequestBinding>() {private fun setOnClick() {binding.requestBaseResponse.setOnClickListener {lifecycleScope.launch {showProgress()val response = viewModel.getWhoIsSuspend("xxhzm.cn")showData(response.toString())}}}
}

7. 文件操作

7.1 文件上传 (FileOperationActivity.kt)

private fun uploadFile(fileUri: MutableList<Uri?>) {try {val files = fileUri.mapNotNull { fileUri ->val file = getFileFromUri(fileUri!!)// ... 文件处理file}val params = mutableMapOf<String, String>()params["param1"] = "value1"params["param2"] = "value2"viewModel.uploadFile(files, params, "files") {handlerData(it as RequestStatus)}} catch (e: Exception) {binding.result.append("文件处理失败: ${e.message}\n")}
}

7.2 文件下载

private fun startDownload(isResume: Boolean) {val fileUrl = "test.zip"if (isResume) {// 断点续传下载viewModel.downloadFileWithResume(fileUrl, 128) {handlerDownload(it as RequestStatus)}} else {// 普通下载viewModel.downloadFile(fileUrl) {handlerDownload(it as RequestStatus)}}
}

8. 缓存机制

通过 @Cacheable 注解实现请求缓存:

@Cacheable(ttl = 30 * 1000,           // 缓存时间30秒strategy = CacheStrategy.CACHE_FIRST,  // 缓存策略includeQueryParams = true  // 包含查询参数
)
suspend fun whois(@Query("domain") domain: String): BaseResponse<Whois?>?

9. 异常处理

通过 CoroutineExceptionHandler 和 RequestStatus 处理请求状态:

result.handle(onDone = {showData(it.toString())},onException = { e ->showError("异常: ${e.message}")},onStart = {showProgress()},onEnd = {hideProgress()}
)

10. 使用建议

  1. 选择合适的请求风格:简单请求可使用挂起函数风格,复杂状态处理建议使用Flow风格
  2. 合理使用缓存:对不常变化的数据添加缓存注解提高性能
  3. 异常处理:在ViewModel和UI层都要做好异常处理
  4. 文件操作:注意文件IO操作应在后台线程执行
  5. 内存管理:及时释放不需要的资源,避免内存泄漏

Demo

通过网盘分享的文件:easynet.zip
链接: https://pan.baidu.com/s/1BSR4vpMMKnm3pCuWQB8HHQ?pwd=fh97 提取码: fh97

http://www.hskmm.com/?act=detail&tid=38801

相关文章:

  • 详细介绍:云栖2025 | 阿里云AI搜索年度发布:开启Agent时代,重构搜索新范式
  • 2025 年最新推荐!吐司面包包装机厂家权威榜发布,含中国烘焙设备协会测评数据与优质企业精选食品装袋封口/面包装袋封口/吐司套袋封口包装机优质厂家提推荐
  • 十月读书笔记_2
  • 基于格的抗量子密码
  • 2025年渣土车洗轮机源头厂家权威推荐榜单:渣土洗轮机/封闭洗车机/工地洗轮机源头厂家精选
  • 测试文档
  • 【华为云 ToB 大模型团队】校园招聘
  • Yolo11Onnx——图像前处理
  • FR报表中调用py接口返回数据成功后,刷新报表报表
  • Yolo11Onnx——图像后处理
  • 【ROS2学习笔记】话题通信篇:python话题订阅与发布 - 详解
  • Aseprite:专业像素艺术与动画制作工具
  • 2025 年不锈钢方管制造厂家最新推荐排行榜:权威测评选出高性能可靠性优质品牌榜单不锈钢矩形管/不锈钢管材/不锈钢异行管/不锈钢毛细管公司推荐
  • 2025年上海机床CE认证公司权威推荐榜单:国外CE认证/快速CE认证/充电桩CE认证源头公司精选
  • 基于Langgraph+Langchain框架实现的旅行规划助手
  • 上古真经:【人不为己,天诛地灭,应该为:人不自指,天诛地灭】
  • 2025 年最新展会展台搭建设计公司推荐排行榜:结合协会测评权威数据的优质服务商甄选榜单聚焦上海/深圳/国外/海外/美国‌/法国/德国/俄罗斯等地公司推荐
  • [Bug]: Your system has an unsupported version of sqlite3. Chroma requires sqlite3 = 3.35.0
  • Linux Ubuntu / Debian / CentOS 下载编译安装最新版 SQLite3 教程
  • P14306 【MX-J27-T3】旋律
  • AI元人文:未来价值权衡的演进图景
  • 2025年电力绝缘围栏制造商权威推荐榜单:绝缘围栏/电力安全围栏/变压器围栏源头厂家精选
  • 2025年五金喷粉直销厂家权威推荐榜单:五金喷涂/喷塑处理/喷塑加工源头厂家精选
  • 责任链模式 - Higurashi
  • P3799题解(枚举)
  • 安卓无线调试
  • B3611 【模板】传递闭包
  • 感知节点@9@ ESP32+arduino+FreeRTOS 第七个程序 读取射频卡卡号
  • QEMU 实现新指令
  • 一文读懂x402 协议