Flutter 13 网络层框架架构设计,支持dio等框架。
在移动APP开发过程中,进行数据交互时,大多数情况下必须通过网络请求来实现。客户端与服务端常用的数据交互是通过HTTP请求完成。面对繁琐业务网络层,我们该如何通过网络层架构设计来有效解决这些问题,这便是网络层框架架构设计的初衷。
设计要求:
1. 支持网络库插拔设计,且不干扰业务层
2. 简洁易用,支持配置来进行请求
3. Adapter设计,扩展性强
4. 统一异常和返回处理

解决问题:
切换成本高:网络操作使用的三方库存在不维护切换成本高的风险;
接口管理不便:对于大中型APP接口众多,不方便管理;
重复代码多:APP中进行数据交互的场景很多,网络请求存在大量的重复代码;
扩展性差:网络操作和业务代码耦合严重,不利于扩展;
开发效率低:不同三方库使用方式不统一,步骤繁琐开发效率低;
一、搭建基础的网络请求框架HiNet
1)创建基础请求
创建抽象的基础请求类BaseRequest,并向上层提供获取请求路径,请求方式等抽象方法,及添加参数和请求头等能力。
base_request.dart
enum HttpMethod { GET, POST, DELETE }/// 基础请求
abstract class BaseRequest {var pathParams;var userHttps = true;/// 域名String authority() {return "api.devio.org";}HttpMethod httpMethod();String path();String url() {Uri uri;var pathStr = path();// 拼接路径参数if (pathParams != null) {if (pathStr.endsWith("/")) {pathStr = "$pathStr$pathParams";} else {pathStr = "$pathStr/$pathParams";}}// http和https的切换if (userHttps) {uri = Uri.https(authority(), pathStr, params);} else {uri = Uri.http(authority(), pathStr, params);}print("url:${uri.toString()}");return uri.toString();}bool needLogin();Map<String, String> params = {};/// 添加参数BaseRequest add(String k, Object v) {params[k] = v.toString();return this;}Map<String, dynamic> header = {};/// 添加请求头BaseRequest addHeader(String k, Object v) {header[k] = v.toString();return this;}
}
2)创建测试请求
创建测试请求类TextRequest继承自 基础请求抽象类BaseRequest,实现请求路径和请求方式等。
test_request.dart
import 'package:hi_net/http/request/base_request.dart';class TestRequest extends BaseRequest{@overrideHttpMethod httpMethod() {return HttpMethod.GET;}@overridebool needLogin() {return false;}@overrideString path() {return "uapi/test/test";}
}
3)创建核心网络请求类
创建核心网络请求类HiNet,提供发送请求,接收响应,解析返回数据,统一错误处理等功能。当前使用模拟响应请求的方式,下面内容会带着大家一步一步进行完善。
hi_net.dart
import 'package:hi_net/http/request/base_request.dart';class HiNet {HiNet._internal();static HiNet? _instance;static HiNet get getInstance {_instance ??= HiNet._internal();return _instance!;}Future fire(BaseRequest request) async {var respones = await send(request);var result = respones['data'];print("result:$result");return result;}Future<dynamic> send<T>(BaseRequest request) {print("url:${request.url()}");print("httpMethod:${request.httpMethod()}");request.addHeader("aaaa", "bbbb");print("header:${request.header}");return Future.value({"statusCode": 200,"data": {"code": 0, "message": "success"}});}
}
4)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行模拟Http请求。
TestRequest testRequest = TestRequest();
testRequest.add("1111", "2222").add("3333", "4444");
HiNet.getInstance.fire(testRequest);
模拟接口请求成功,输出log:

二、增加统一异常和响应数据处理,及Adapter模式设计
1)创建网络异常统一格式类
创建网络异常统一格式类HiNetError,包含code、message和data信息。
创建登录异常NeedLogin 和 授权异常NeedAuth 继承自HiNetError。
hi_net_error.dart
/// 需要登录的异常
class NeedLogin extends HiNetError {NeedLogin({int code = 401, String message = "请先登录"}) : super(code, message);
}/// 需要授权的异常
class NeedAuth extends HiNetError {NeedAuth(String message, {int code = 403, dynamic data}): super(code, message, data: data);
}/// 网络异常统一格式类
class HiNetError implements Exception {final int code;final String message;final dynamic data;HiNetError(this.code, this.message, {this.data});
}
2)创建统一网络层返回格式
创建统一网络层返回格式HiNetResponse,包含request、statusCode、statusMessage和data等信息。
hi_net_adapter.dart
/// 统一网络层返回格式
class HiNetResponse<T> {HiNetResponse({this.data,this.request,this.statusCode,this.statusMessage,this.extra});T? data;BaseRequest? request;int? statusCode;String? statusMessage;dynamic extra;@overrideString toString() {if (data is Map) {return json.encode(data);}return data.toString();}
}
3)创建网络请求抽象类
网络请求抽象类HiNetAdapter,提供发送请求能力。
hi_net_adapter.dart
import 'dart:convert';import 'package:hi_net/http/request/base_request.dart';/// 网络请求抽象类
abstract class HiNetAdapter {Future<HiNetResponse<T>> send<T>(BaseRequest request);
}/// 统一网络层返回格式
class HiNetResponse<T> {HiNetResponse({this.data,this.request,this.statusCode,this.statusMessage,this.extra});T? data;BaseRequest? request;int? statusCode;String? statusMessage;dynamic extra;@overrideString toString() {if (data is Map) {return json.encode(data);}return data.toString();}
}
4)创建测试适配器
创建测试适配器MockAdapter ,mock数据。
mock_adapter.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';/// 测试适配器,mock数据
class MockAdapter extends HiNetAdapter {@overrideFuture<HiNetResponse<T>> send<T>(BaseRequest request) {return Future.delayed(const Duration(milliseconds: 1000), () {return HiNetResponse(data: {"code": 0, "message": "success"} as T, statusCode: 200);});}
}
5)完善核心网络请求类
完善核心网络请求类HiNet,使用mock适配器MockAdapter发送请求,使用HiNetResponse接收请求响应数据,增加统一错误处理。
hi_net.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/core/mock_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';class HiNet {HiNet._internal();static HiNet? _instance;static HiNet get getInstance {_instance ??= HiNet._internal();return _instance!;}Future fire(BaseRequest request) async {HiNetResponse? response;var error;try {response = await send(request);} on HiNetError catch (e) {error = e;response = e.data;print("HiNetError:${e.message}");} catch (e) {// 其他错误error = e;print("OtherError:$e");}if (response == null) {print("error:$error");}var result = response?.data;print("result:$result");var status = response?.statusCode ?? 0;switch (status) {case 200:return result;case 401:throw NeedLogin();case 403:throw NeedAuth(result.toString(), data: result);default:throw HiNetError(status, result.toString(), data: result);}}Future<dynamic> send<T>(BaseRequest request) {print("url:${request.url()}");/// 使用mock发送请求HiNetAdapter adapter = MockAdapter();return adapter.send(request);}
}
6)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行模拟Http请求。
TestRequest testRequest = TestRequest();testRequest.add("1111", "2222").add("3333", "4444");try{var result = await HiNet.getInstance.fire(testRequest);print(result);} on NeedAuth catch (e) {print(e);} on NeedAuth catch (e) {print(e);} on HiNetError catch (e) {print(e);}
模拟接口请求成功,输出log:

三、 扩展hi_net添加对dio的支持
1)创建dio适配器
创建dio适配器DioAdapter,重写发送请求的send方法,采用dio框架进行真正的Http网络请求;根据服务器响应数据构建HiNetError 和 HiNetResponse。
添加dio依赖:
pubspec.yaml
dio: ^5.7.0
dio_adapter.dart
import 'package:dio/dio.dart';
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/request/base_request.dart';/// Dio适配器
class DioAdapter extends HiNetAdapter {@overrideFuture<HiNetResponse<T>> send<T>(BaseRequest request) async {Response? response;var error, options = Options(headers: request.header);try {if (request.httpMethod() == HttpMethod.GET) {response = await Dio().get(request.url(), options: options);} else if (request.httpMethod() == HttpMethod.POST) {response = await Dio().post(request.url(), data: request.params, options: options);} else if (request.httpMethod() == HttpMethod.DELETE) {response = await Dio().delete(request.url(), data: request.params, options: options);}} on DioError catch (e) {error = e;response = e.response;}if (error != null) {/// 抛出HiNetError异常throw HiNetError(response?.statusCode ?? -1, error.toString(),data: buildResponse(response, request));}return buildResponse(response, request);}/// 构建HiNetResponseHiNetResponse<T> buildResponse<T>(Response? response, BaseRequest request) {return HiNetResponse(data: response?.data as T,request: request,statusCode: response?.statusCode,statusMessage: response?.statusMessage,extra: response);}
}
2)使用dio发送请求
修改HiNet 的send方法,使用dio适配器发送请求;Adapter设计,可轻便的更换三方网络请求库,加强了网络请求架构的扩展性。
hi_net.dart
import 'package:hi_net/http/core/hi_net_adapter.dart';
import 'package:hi_net/http/core/hi_net_error.dart';
import 'package:hi_net/http/core/adapter/mock_adapter.dart';
import 'package:hi_net/http/request/base_request.dart';import 'adapter/dio_adapter.dart';///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {Future<dynamic> send<T>(BaseRequest request) {print("url:${request.url()}");/// 使用mock发送请求/// HiNetAdapter adapter = MockAdapter();/// 使用dio发送请求HiNetAdapter adapter = DioAdapter();return adapter.send(request);}
}
3)发送测试请求
创建TestRequest测试请求对象,使用HiNet网络请求核心单例类进行Http请求。
注意:该测试接口requestPrams是必传字段,不传接口会返回失败。
TestRequest testRequest = TestRequest();testRequest.add("1111", "2222").add("3333", "4444").add("requestPrams", "5555");try{var result = await HiNet.getInstance.fire(testRequest);print("testRequest result: $result");} on NeedAuth catch (e) {print(e);} on NeedAuth catch (e) {print(e);} on HiNetError catch (e) {print(e);}
Http接口请求成功,输出log:

Http接口请求失败,输出log:

四、JSON编码器和解码器
1)使用 json_serializable 框架
使用 json_serializable 框架,对JSON数据进行解析。
添加 json_serializable 依赖:
pubspec.yaml
json_serializable: ^6.8.0json_annotation: ^4.9.0build_runner: ^2.1.11
2)创建测试接口返回数据bean
测试接口返回接送数据:
{code: 0, method: GET, requestPrams: 5555}
创建TestModel,编写好属性、构造方法、TestModel.fromJson() 和 toJson();增加注解:@JsonSerializable()
test_model.dart
import 'package:json_annotation/json_annotation.dart';part 'test_model.g.dart';/// 测试接口返回数据bean
/// {code: 0, method: GET, requestPrams: 5555}
@JsonSerializable()
class TestModel {int code;String method;String requestPrams;TestModel(this.code, this.method, this.requestPrams);//固定格式,不同的类使用不同的mixin即可factory TestModel.fromJson(Map<String, dynamic> json) => _$TestModelFromJson(json);//固定格式,Map<String, dynamic> toJson() => _$TestModelToJson(this);
}
执行 :dart run build_runner build

提示:需要在 TestModel 增加 part 'test_model.g.dart';
加上后再次执行 :dart run build_runner build ,接口自动生成 test_model.g.dart 文件

总结:使用 json_serializable 框架构建JSON解析类时,手动创建的基类Model(TestModel),必须满足以下要求:
1. 在Model类编写 part '同Model类文件名.g.dart'; 如 TestModel类文件名est_model.dart ,则编写 part 'test_model.g.dart';
2.增加注解: @JsonSerializable()
3.编写固定格式的代码,名字换成基类名字,这里是TestModel:
//固定格式,不同的类使用不同的mixin即可factory TestModel.fromJson(Map<String, dynamic> json) => _$TestModelFromJson(json);//固定格式,Map<String, dynamic> toJson() => _$TestModelToJson(this);
3)使用
TestRequest testRequest = TestRequest();testRequest.add("1111", "2222").add("3333", "4444").add("requestPrams", "5555");try{var result = await HiNet.getInstance.fire(testRequest);var testModel = TestModel.fromJson(result['data']);print("testRequest requestPrams: ${testModel.requestPrams}");} on NeedAuth catch (e) {print(e);} on NeedAuth catch (e) {print(e);} on HiNetError catch (e) {print(e);}
JSON解析,输出log:

至此,完成了网络请求框架的基本功能,持续完善。。。
相关文章:
Flutter 13 网络层框架架构设计,支持dio等框架。
在移动APP开发过程中,进行数据交互时,大多数情况下必须通过网络请求来实现。客户端与服务端常用的数据交互是通过HTTP请求完成。面对繁琐业务网络层,我们该如何通过网络层架构设计来有效解决这些问题,这便是网络层框架架构设计的初…...
Python小白学习教程从入门到入坑------第二十课 闭包修饰器(语法基础)
一、递归函数 1.1 基本信息 递归函数是指一个函数在其定义中直接或间接地调用了自身 递归在解决许多问题(如树的遍历、图的搜索、数学中的分治算法等)时非常有用 在Python中,递归函数可以通过简单的语法来实现 然而,使用递归…...
Vue+element-ui实现网页右侧快捷导航栏 Vue实现全局右侧快捷菜单功能组件
Vue+element-ui实现网页右侧快捷导航栏 Vue实现全局右侧快捷菜单功能组件 可视区域没超过当前屏幕高度时候只显示三个菜单效果 可视区域超过当前屏幕高度时,显示可回到顶部菜单的,当然这个菜单显示条件可以自定义,根据需求设置 然后将这个整体功能创建为一个全局组件 代…...
如何配置,npm install 是从本地安装依赖
在 Node.js 中,要使npm install从本地安装依赖,可以按照以下步骤进行配置: 一、准备本地依赖包 确保你有本地的依赖包。这个依赖包可以是一个包含package.json文件的文件夹,或者是一个已经打包好的.tgz文件。 二、使用相对路径…...
Python画图3个小案例之“一起看流星雨”、“爱心跳动”、“烟花绚丽”
源码如下: import turtle # 导入turtle库,用于图形绘制 import random # 导入random库,生成随机数 import math # 导入math库,进行数学计算turtle.setup(1.0, 1.0) # 设置窗口大小为屏幕大小 turtle.title("流星雨动画&…...
Knife4j配置 ▎使用 ▎教程 ▎实例
knife4j简介 支持 API 自动生成同步的在线文档:使用 Swagger 后可以直接通过代码生成文档,不再需要自己手动编写接口文档了,对程序员来说非常方便,可以节约写文档的时间去学习新技术。 提供 Web 页面在线测试 API:光有文档还不够,Swagger 生成的文档还支持在线测试.参数和格式都…...
电子电气架构 --- 车载芯片现状
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧…...
Unity 二次元三渲二
三渲二 注意:Unity必须是2022.3LTS及以上和URP项目!!! 下载三渲二插件 【如何将原神的角色导入Unity】全网最细致教程,全程干货。不使用任何收费插件,使用Spring Bone对头发和衣服进行物理模拟。_原神 步…...
echart实现地图数据可视化
文章目录 [TOC](文章目录) 前言一、基本地图展示2.数据可视化 总结 前言 最近工作安排使用echarts来制作图形报表,记录一下我的步骤,需求呈现一个地图,地图显示标签,根据业务指标值给地图不同省市填充不同颜色,鼠标放…...
网关三问:为什么微服务需要网关?什么是微服务网关?网关怎么选型?
文章整体介绍 本文旨在解答关于微服务网关的三个核心问题: 1)为什么需要网关?也即在何种场景下应采用微服务网关以优化系统架构; 2)什么是微服务网关?主要讲构成微服务网关的关键能力,包括但…...
Mybatis-plus解决兼容oracle批量插入
本博客借鉴网上很多大佬的答案,东拼西凑,最终在项目中完成批量插入,仅供参考~~~ 1. 自定义SQL注入器 新建一个名为EasySqlInjector的类,继承DefaultSqlInjector。 public class EasySqlInjector extends DefaultSqlInjector {O…...
Kaggle竞赛——灾难推文分类(Disaster Tweets)
目录 1. 准备工作2. 资源导入3. 数据处理4. 绘制词云图5. 数据可视化5.1 词数和字符数可视化5.2 元特征可视化5.3 类别可视化 6. 词元分析6.1 一元语法统计6.2 多元语法统计 7. 命名实体识别8. 推文主题提取9. 构建模型9.1 数据划分与封装9.2 模型训练与验证 10. 模型评估11. 测…...
SC2601音频编解码器可pin to pin兼容ES8311
SC2601 是一款低功耗单声道音频编解码器,具有全差分输出,支持在全差分配置下可编程模拟输入。可pin to pin兼容ES8311。 录音路径包含一个全差分输入,低噪声可编程增益放大器和自动增益控制(ALC)。在录音过程中,通过内…...
通用AT指令
1、查询SIM卡状态 ATCPIN?2、查询信号强度 ATCSQ //99,99 表示无信号3、查询IMEI ATCGSN4、查询4G/5G模式 ATCOPS? //7表示在4G模式,13表示在5G模式5、设置接入点 ATCGDCONT1,"IP","uninet" //联通 ATCGDCONT1,"IP","…...
二进制狼群算法
本文所涉及所有资源均在 传知代码平台 可获取。 目录 一、背景及意义介绍 背景 意义...
STL——list的介绍和使用
前言 本篇博客我们继续来介绍STL的内容,这次我们要介绍的是list这个容器,可以简单地理解为顺序表,当然和我们之前学过顺序表还是有区别的,具体内容大家可以继续往下阅读,下面进入正文。 1. list简介 1.list是一种可…...
二百七十六、ClickHouse——Hive和ClickHouse非常不同的DWS指标数据SQL语句
一、目的 在完成数据之后对业务指标进行分析,Hive和ClickHouseSQL真不一样 二、部分业务指标表 2.1 统计数据流量表1天周期 2.1.1 Hive中原有代码 2.1.1.1 Hive中建表语句 --1、统计数据流量表——动态分区——1天周期 create table if not exists hurys_d…...
Elasticsearch Date类型,时间存储相关说明
本文介绍了在SpringBoot中处理Elasticsearch中日期时间格式的问题。当时间输出为UTC格式并存在时区差异时,可通过设置字段格式如yyyy-MM-dd HH:mm:ss并指定时区为GMT8来解决。存储Date类型数据时,可以使用JSON库如json-lib, fastjson, Jackson或gson进行…...
mathorcup2024台风 我all in ai
三个问题,力大砖飞。 不建物理模型,直接all in好吧 第一个故意无监督 第二个LSTMCNN注意力,刚好时间空间 第三个在第二个上加了个transfomer ,然后LSTM变双向,增加层数(基线模型选的经验公式,少…...
android 10 后台启动activity
摘要:Android 10(API 级别 29)及更高版本会限制应用何时可以启动 activity 背景。这些限制有助于最大限度地减少对用户的干扰, 让用户能够更好地控制其屏幕上显示的内容。本文以此为出发点,基于展锐平台对系统代码进行…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
【阅读笔记】MemOS: 大语言模型内存增强生成操作系统
核心速览 研究背景 研究问题:这篇文章要解决的问题是当前大型语言模型(LLMs)在处理内存方面的局限性。LLMs虽然在语言感知和生成方面表现出色,但缺乏统一的、结构化的内存架构。现有的方法如检索增强生成(RA…...
