Flutter快学快用15 服务通信:Flutter 中常见的网络协议

news/2024/5/1 20:30:42/文章来源:https://blog.csdn.net/fegus/article/details/126969595

上一课时之前,我们的接口都是在代码中模拟假数据,并没有从服务端获取数据,但是在实际开发中,必须与服务端进行交互。本课时主要介绍在 Flutter 中常见的网络传输协议序列化方式,并对其中比较常用的协议进行简单实践,最后再通过 JSON 协议来完善本课时的 api 部分的代码。

常见的 APP 网络传输协议序列化方式

常见的传输协议有三种:XML 、JSON 和 Protocol Buffer。我们先来对比下这三种协议,我会分别从 Flutter 中的实现、序列化后的数据长度、Flutter 中反序列化性能三个方面来讲解。我先将本课时中的一段基础的数据格式用来做效果演示,测试数据如下:

nickName = 'test-pb'; 
uid = '3001'; 
headerUrl = 'http://image.biaobaiju.com/uploads/20180211/00/1518279967-IAnVyPiRLK.jpg';

上面的是用户信息接口,接下来我们使用这三种方式来实现这个接口。

XML

XML 指可扩展标记语言(eXtensible Markup Language)是一种通用的重量级数据交换格式,以文本结构存储。

在 Flutter 中有一个解析 XML 的第三方库 xml2json,将服务端的 XML 解析为 JSON 格式,因为是第三方库,因此需要在 pubspec.yaml 中增加该库的依赖,然后更新本地库。接下来我们实现具体的代码,在 lib 目录下新建 api_xml ,然后在目录下创建 api_xml/user_info/index.dart 。创建完成后,我们来实现 user_info/index.dart 的逻辑。

首先需要增加第三方库的引用。

import 'dart:convert'; 
import 'package:two_you_friend/util/struct/user_info.dart'; 
import 'package:xml2json/xml2json.dart';

接下来实现 ApiXmlUserInfoIndex 类中的 getSelfUserInfo 方法,后续 getSelfUserInfo 会是一个异步网络请求方法,因此将返回类型修改为 Future<StructUserInfo>,具体实现逻辑如下:

/// 获取自己的个人信息 
static Future<StructUserInfo> getSelfUserInfo() async{ // 模拟xml假数据 final userInfoXml = '''<?xml version="1.0"?> <userInfo> <nickName>test</nickName> <uid>3001</uid> <headerUrl>http://image.biaobaiju.com/uploads/20180211/00/1518279967-IAnVyPiRLK.jpg</headerUrl> </userInfo>'''; 

// 记录当前时间
int currentTime = new DateTime.now().microsecondsSinceEpoch;
Xml2Json xml2json = Xml2Json();
xml2json.parse(userInfoXml);
// 转化xml数据
final userInfoStr = xml2json.toGData();
print(‘xml length’);
print(userInfoStr.length);

int jsonStartTime = new DateTime.now().microsecondsSinceEpoch;
final userInfo = json.decode(userInfoStr);
// 打印解析json时间
print(‘json decode time’);
print(new DateTime.now().microsecondsSinceEpoch - jsonStartTime);

// 打印整体解析时间
print(‘xml decode time’);
print(new DateTime.now().microsecondsSinceEpoch - currentTime);
return StructUserInfo(
userInfo[‘userInfo’][‘uid’][‘$t’] as String,
userInfo[‘userInfo’][‘nickName’][‘$t’] as String,
userInfo[‘userInfo’][‘headerUrl’][‘$t’] as String
);
}

上述代码首先在第 4 行模拟一个 XML 数据,在第 12 行记录开始解析时间,第 28 行打印整体 XML 解析时间,在第 24 行打印 JSON 的解析时间。XML 的解析过程是先将 XML 转化为一个 JSON 字符串,然后再通过 convert 转化为 JSON。在 main.dart 中引入该文件,并调用 getSelfUserInfo 方法,可以看到如下的打印信息。

flutter: xml length 
flutter: 180 
flutter: json decode time 
flutter: 200 
flutter: xml decode time 
flutter: 2000

从解析过程来看,XML 的解析性能肯定是比较差的,因为最终还是需要将 XML 转化为 JSON 来处理,接下来我们看下 JSON 的解析实现方式。

JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。 易于人阅读和编写,同时也易于机器解析和生成。 它是基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999 的一个子集。

在 Flutter 中,JSON 解析有专门的 dart 原生库支持——dart:convert。同样我们去实现 XML 例子中的 user_info/index.dart,我们以 api/user_info/index.dart 为例子来实现,在原来代码基础上,我们增加打印解析时间和 JSON 长度,具体代码如下:

/// 获取自己的个人信息 static Future<StructUserInfo> getSelfUserInfo() async{ String jsonStr = '{"nickName":"test","uid":"3001","headerUrl":"http://image.biaobaiju.com/uploads/20180211/00/1518279967-IAnVyPiRLK.jpg"}'; print('json length'); print(jsonStr.length); int currentTime = new DateTime.now().microsecondsSinceEpoch; final jsonInfo = json.decode(jsonStr) as Map<String, dynamic>; print('json parse time'); print(new DateTime.now().microsecondsSinceEpoch - currentTime); return StructUserInfo.fromJson(jsonInfo); }

上面代码较 XML 简单一些,第 3 行创建假数据,然后在第 7 行进行解析。在代码第 5 行,打印 JSON 长度,第 9 行打印具体的解析时间,在 main.dart 执行该函数,可以看到如下打印数据。

flutter: json length 
flutter: 119 
flutter: json parse time 
flutter: 420

与 XML 对比,从解析时间和传递数据长度来看,都是较优的,接下来我们看下 Protocol Buffer 的实现、相关解析时长和具体的数据长度。

Protocol Buffer

Protocol Buffer 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式,可用于通信协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

在 Flutter 中应用 Protocol Buffer 需要下面几个过程。

1.安装 Protocol Buffer 工具,在 Mac 应用如下命令。

brew install protobuf

如果是在 Windows 或者 Linux 上,则前往( https://github.com/protocolbuffers/protobuf/releases?after=v3.5.0)解压安装即可。

2.安装 protoc_plugin 插件,在 Mac 或者 Linux 应用如下命令安装。

pub global activate protoc_plugin

如果在 Windows 上没有这个支持,你在 Windows 上只能通过虚拟机的方式。

3.在 lib 同级目录下创建 protos 用来存放所需要的 Protocol Buffer 文件,这里我们创建了一个 user_info.proto ,然后添加下面的代码:

syntax = "proto3"; 
option java_package = "pro.two_you_friend"; 
message UserInfoRsp { string nickName = 1; string headerUrl = 2; string uid = 3; 
}

上面的代码就是创建一个 Protocol Buffer 协议,该协议数据结构就是一个 UserInfo 的结构,具体关于 Protocol Buffer 的协议,可以参考官网。

4.创建完成 Protocol Buffer 协议后,我们再将 Protocol Buffer 文件转化为 Dart 文件,在项目根目录,也就是 lib 同级目录,运行下面命令。

protoc --dart_out=./lib ./protos/* --plugin=protoc-gen-dart=$HOME/.pub-cache/bin/protoc-gen-dart

其中 dart_out 就是转化后的 dart 文件存放路径,会默认带上原有 protos 目录。--plugin 就是需要使用到的插件,这里的路径就是第二步安装的插件位置。

5.运行成功后,会在 lib 目录下创建 protos 目录,并生成如图 1 的目录结构;

image (14).png

图 1 生成的 Protocol Buffer 目录结构

生成完成以后,这时候是会提示报错的,因为在 user_info.pb.dart 中引用了 package:protobuf/protobuf.dart 这个库。接下来我们就需要去修改 pubspec.yaml ,添加 Protocol Buffer( protobuf: ^1.0.1 )第三方库的依赖,添加完成后更新本地库。

以上就完成了整个 Protocol Buffer 的创建到转化,接下来我们看下如何在 Flutter 应用,同样和 XML 以及 JSON 一样,我们继续在 lib 目录下新建一个 api_pb 文件夹,用来存放 Protocol Buffer 相关的 API 协议,这里为了演示,只创建 api_pb/user_info/index.dart 。接下来我们看下具体的代码逻辑。

先引入相应的库文件,其中第 2 行就是相应的 Protocol Buffer 文件。

import 'package:two_you_friend/util/struct/user_info.dart'; 
import 'package:two_you_friend/protos/user_info.pb.dart';

接下来我们看下 ApiPbUserInfoIndex 类中创建 Protocol Buffer 的代码部分,这部分逻辑放在 createUserInfo 函数中,具体代码如下:

/// 生成二进制内容,测试文件 
static List<int> createUserInfo() { UserInfoRsp userInfoRsp = UserInfoRsp(); userInfoRsp.nickName = 'test'; userInfoRsp.uid = '3001'; userInfoRsp.headerUrl = 'http://image.biaobaiju.com/uploads/20180211/00/1518279967-IAnVyPiRLK.jpg'; List<int> retInfo = userInfoRsp.writeToBuffer(); return retInfo; 
}

代码的第 2 行就是创建 Protocol Buffer 中的 Message 类,也就是我们的 UserInfo 数据结构,然后根据其数据结构,设置具体的字段值,最后调用 writeToBuffer 转化为二进制数据。

应用上面生成的二进制数据,我们再来实现 getSelfUserInfo 方法,具体代码如下:

/// 获取自己的个人信息 
static Future<StructUserInfo> getSelfUserInfo() async{ // 该数据涞源createUserInfo函数 int currentTime = new DateTime.now().microsecondsSinceEpoch; UserInfoRsp userInfoRsp = UserInfoRsp.fromBuffer( [ 10, 4, 116, 101, 115, 116, 18, 72, 104, 116, 116, 112, 58, 47, 47, 105, 109, 97, 103, 101, 46, 98, 105, 97, 111, 98, 97, 105, 106, 117, 46, 99, 111, 109, 47, 117, 112, 108, 111, 97, 100, 115, 47, 50, 48, 49, 56, 48, 50, 49, 49, 47, 48, 48, 47, 49, 53, 49, 56, 50, 55, 57, 57, 54, 55, 45, 73, 65, 110, 86, 121, 80, 105, 82, 76, 75, 46, 106, 112, 103, 26, 4, 51, 48, 48, 49 ] ); print('pb length'); print(userInfoRsp.toString().length); int dfTime = new DateTime.now().microsecondsSinceEpoch - currentTime; print('pb decode time'); print(dfTime); return StructUserInfo( userInfoRsp.uid, userInfoRsp.nickName, userInfoRsp.headerUrl ); 
}

代码第 5 行是应用 createUserInfo 生成的二进制数据,利用该二进制数据调用 fromBuffer 转化为 Protocol Buffer 对象,返回的对象可以直接获取到 StructUserInfo 的相应字段: uid、nickName 和 headerUrl,具体代码在第 25 到第 28行。第 18 行打印字符串长度,第 23 行打印反序列化时间。运行上面的代码,可以看到如下打印数据:

flutter: pb length 
flutter: 109 
flutter: pb decode time 
flutter: 383

长度和解析时长相对 JSON 协议又减少了一些,因此在带宽和解析性能方面都是优于 JSON 和 XML。由于本课时中还没有实现服务端代码,我们只能借助第三方 Mock 平台来实现网络调用,因此这里会以 JSON 协议为参考来实践本课时的 api 层代码逻辑。在实际应用中,我更倾向大家使用 Protocol Buffer 。

以上就是三种协议在 Flutter 中的应用尝试和对比,基于数据长度和解析性能对比(由于跑的数据总量不够大,因此单次运行会存在样本误差),XML 是最差的,JSON 相对较好,Protocol Buffer 是最优的,不过可读性最差,具体对比看下表格 1。

image (15).png

表格 1 整体数据对比情况

代码实践

介绍完常见的网络传输协议序列化方式,接下来就使用 JSON 的传输协议来完善我们 api 逻辑。这里会应用到一个第三方的 Mock 平台。主要是 Mock 以下几个接口协议,如图 2 所示的结构列表。

image (16).png

图 2 Mock 协议列表

有了具体协议 Mock 协议后,我们再来实现 Flutter 中的代码。首先我们需要创建一个通用的网络请求的类,这个类我们存放在 util/tools 目录下,命名为 call_server.dart 。Flutter 中的网络协议需要使用到 dio 这个第三方库,同样还是需要在 pubspec.yaml 增加依赖,然后更新本地库文件。接下来我们看下 call_server.dart 的代码实现。

通用网络请求类实现

该通用网络请求类,文件存放在源码中的 lib/util/tools/call_server.dart ,接下来我们看下它的实现逻辑。

首先还是引入相应的库文件

import 'dart:convert'; 
import 'package:dio/dio.dart'; 
import 'package:two_you_friend/util/tools/json_config.dart';

第 1 个库是数据转化类的原生库,第 2 个库是网络请求库,第 3 个库是我们自己实现的一个工具库,该库的作用是读取一个 JSON 配置文件。

接下来我们实现 CallServer 类,在类中新增一个 get 方法,这里需要注意因为 dio 网络请求是一个异步方法,因此这里需要将 get 设计为一个 async 的方法,并返回的是一个 Future 类型,具体代码如下:

/// 统一调用API接口 
class CallServer { /// get 方法 static Future<Map<String, dynamic>> get } 
}

因为网络请求异步返回的是一个 JSON 协议,因此需要设置返回的数据结构为 Map<String, dynamic> 。接下来我们看下具体的函数代码逻辑。

// 根据类型,获取api具体信息 
Map<String, dynamic> apis = await JsonConfig.getConfig('api'); 
if(apis == null) { return {"ret" : false}; 
} 
String callApi = apis[apiName]['apiUrl'] as String; 
// 处理异常情况 
if(callApi == null) { return {"ret" : false}; 
} 
// 处理参数替换 
if(params != null) { params.forEach((k, v) => callApi = callApi.replaceAll('{$k}', '$v')); 
} 
// 调用服务端接口获取返回数据 
try { Response response = await Dio().get( callApi, options: Options(responseType: ResponseType.json) ); Map<String, dynamic> retInfo = json.decode(response.toString()) as Map<String, dynamic>; return retInfo; 
} catch (e) { return {"ret" : false}; 
}

第 2 行读取配置文件中的 api.json 数据(配置文件需要在 pubspec.yaml 中引入,具体查看源码中的第 55 和第 56 行),该 api.json 的部分数据如下:

{ "recommendList" : { "method" : "get", "apiUrl" : "https://www.fastmock.site/mock/978685eaf6950d1e2f0790f85cfdacaa/cgi-bin/recommend_list", "params" : null } 
}

其中 JSON 部分就包括了协议名称,以及协议的请求方式和协议的 URL 以及具体的参数。
在 get 方法中,获取到 api.json 数据后,再根据协议名称,获取到协议的 URL 。接下来经过一定的数据判断和参数处理,应用 dio 模块发起 get 网络请求。最后再使用 convert 库,将结构转化为 JSON 数据结构,并返回给到调用方。

ApiContentIndex 实现

通用网络请求实现后,我们再看下具体的接口调用方的实现逻辑。接下来我们修改 ApiContentIndex 中的 getRecommendList 的代码,将原来的假数据转化为网络请求。因为是异步方法,因此还是需要使用 Future 和 async ,函数代码如下:

import 'package:two_you_friend/util/struct/api_ret_info.dart'; 
import 'package:two_you_friend/util/struct/content_detail.dart'; 
import 'package:two_you_friend/util/tools/call_server.dart'; 
/// 获取内容详情接口 
class ApiContentIndex { /// 拉取用户内容推荐帖子列表 Future<StructApiContentListRetInfo> getRecommendList([lastId = null]) async { 

}
}

代码第一部分还是引入相应的库,第二部分创建 ApiContentIndex 类,并创建 getRecommendList 函数,该函数异步返回 StructApiContentListRetInfo 数据结构,支持可选参数 lastId ,有 lastId 则拉取下一页,没有则拉取首页内容。接下来看下 getRecommendList 函数的具体逻辑,代码如下。

/// 拉取用户内容推荐帖子列表 
Future<StructApiContentListRetInfo> getRecommendList([lastId = null]) async { if (lastId != null) { Map<String, dynamic> retJson = await CallServer.get('recommendListNext', {lastId: lastId}); return StructApiContentListRetInfo.fromJson(retJson); } else { Map<String, dynamic> retJson = await CallServer.get('recommendList'); return StructApiContentListRetInfo.fromJson(retJson); } 
}

以上代码就比较简洁了,先根据 lastId 判断拉取首页还是拉取下一页,如果拉取首页,则调用 recommendList 协议,如果拉取下一页,则调用 recommendListNext 协议。使用 CallServer.get 方法与服务端交互,得到返回数据结构后,调用 StructApiContentListRetInfo.fromJson 转化为 StructApiContentListRetInfo 数据结构,这样就实现了具体的 API 协议,最后我们再来看下在页面中调用 api 的使用方法。

HomePageIndex

因为 ApiContentIndex 协议是在 HomePageIndex 这个类中调用,我们就来看下这块的处理逻辑,相同部分我们就不过多介绍。

/// 处理首次拉取和刷新数据获取动作 
void setFirstPage() { ApiContentIndex().getRecommendList().then((retInfo){ if (retInfo.ret != 0) { // 判断返回是否正确 error = true; return; } setState(() { error = false; contentList = retInfo.data; hasMore = retInfo.hasMore; isLoading = false; lastId = retInfo.lastId; }); }); 
}

在 setFirstPage 中调用类 ApiContentIndex 中的异步方法 getRecommendList ,在 getRecommendList 回调中成功获取数据后使用 setState 更新页面状态。由于网络请求有时间延迟,因此在页面刚加载时,需要使用 loading 组件,需要更改原来的 build 方法,修改部分如下:

if (error) { return CommonError(action: this.setFirstPage); 
} 
if(contentList == null){ return Loading(); 
}

主要是第 4 行,增加了对数据的判断,如果为空则显示 loading 组件内容,具体效果如下图 3 所示。

20200717_233752.gif

图 3 网络请求 loading 效果

以上就完成了 ApiContentIndex 部分的 getRecommendList 逻辑,其他代码逻辑基本相似,具体大家可以参考 github 上的源码。

总结

本课时介绍了 APP 常用的三种网络传输协议序列化方式,其次介绍了 Flutter 与服务端的网络通信方法,并且通过传输协议与服务端进行交互获取数据。学完本课时后要着重掌握 JSON 和 Protocol Buffer 的使用方法,其次掌握网络请求库 CallServer 的实现原理。

下一课时我们将整理我们在 Two You APP 研发过程中所涉及的布局逻辑,介绍在 Flutter 中常见的一些布局原理和思想,并用此理论来完善我们 APP 内的“客人态页面” 的功能。

点击此链接查看本课时源码


精选评论

*斌:

StructApiContentListRetInfo.fromJson(Map">, dynamic json) : ret = json[‘ret’] as int, message = json[‘message’] as String, hasMore = json[‘hasMore’] as bool, lastId = json[‘lastId’] as String, data = getContentDetailList(json[‘data’] as List);老师这个fromJson 后面跟:不理解啥意思

    讲师回复:

    这个目的是将 JSON 数据结构转化为 Struct 结构,fromJson 的作用就是这个目的,在 Dart 中是一个强数据类型的语言,不建议直接用动态数据类型 JSON,而是将 JSON 转化为 Struct 结构,这两个之间的桥梁就是 fromJson。

*艺:

第一个lastId应该是作为key的应该要加隐号吧

    讲师回复:

    可以不用加的呢,在 dart 语法中 map 结构的 key 可以不加引号,加上也没什么问题。

*蛋:

老师,报异常:[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: Unable to load asset: assets/json/api.json

    讲师回复:

    你检查下,有没有按照我说的步骤,特别是需要在pubspec.yaml中增加配置文件引入部分。其次你看下这个路径下是否存在api.json文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_11443.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据培训技术phoenix表操作

phoenix表操作 1 显示所有表 &#xff01;table 或 &#xff01;tables 2 创建表 CREATE TABLE IF NOT EXISTS us_population ( State CHAR(2) NOT NULL, City VARCHAR NOT NULL, Population BIGINT CONSTRAINT my_pk PRIMARY KEY (state, city)); 如下显示&#xff1a; 在p…

超级基础篇_疑惑实验

1、多态&#xff1a; 多态是什么&#xff1f; 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作多态的优点 1.消除类型之间的耦合关系 2. 可替换性 3. 可扩充性 …

树的应用 —— 二叉树:二叉树的性质

树的应用 —— 二叉树 二叉树&#xff08;Binary Tree&#xff09;是n &#xff08;n ≥0&#xff09;个节点构成的集合&#xff0c;或为空树&#xff08;n 0&#xff09;&#xff0c;或为非空树。 对于非空树T &#xff0c;要满足&#xff1a; ①有且仅有一个被称为根的节点…

FFmpeg入门详解之20:视频编码原理简介

视频为何需要压缩&#xff1f; 原因&#xff1a;未经压缩的数字视频的数据量巨大 ● 存储困难 ○ 一G只能存储几秒钟的未压缩数字视频。 ● 传输困难 ○ 1兆的带宽传输一秒的数字电视视频需要大约4分钟。 主要压缩了什么东西&#xff1f; 原始视频压缩的目的是去除冗余信息&a…

结构体的理解

结构体前言结构体&#xff1f;定义变量如何赋初值&#xff1f;结构体的访问结构体的嵌套使用注意事项结构体的大小内存对齐默认对齐数的修改为什么存在内存对齐&#xff1f;结构体传参位段什么是位段&#xff1f;位段的内存分配深入剖析位段“存”数据位段的“取”位段的跨平台…

Idea工具中,使用Mapper对象有红线

背景&#xff1a; IDEA开发工具&#xff0c;springboot mybatis项目 &#xff08;这个是不需要改的&#xff0c;也不算是问题&#xff0c;因为项目并不会报错&#xff0c;只是作者好奇找了下问题&#xff0c;并记录一下&#xff09; 问题描述 mapper对象在service层有红线&a…

8 位卷王!总结 1135 页 Java 核心面试手册,硬钢 BATJ 一线大厂面试官

又到了金九银十求职季&#xff01; HR 开始拼业绩&#xff0c;招聘网站也开始释放出大量岗位&#xff0c;转行跳槽、毕业求职的人都开始行动起来&#xff01; 此时&#xff0c;对于大多数程序员来说&#xff0c;最大的目标就是&#xff1a;进大厂&#xff01; 大厂为什么这么…

ArcGIS Map Sdk for unity使用

本文主要讨论离线模式。 目录 1.底图tpk文件制作 2.3D图层slpk文件制作 3.导入使用 1.底图tpk文件制作 软件&#xff1a;91卫图助手 Arcgis Pro 操作步骤&#xff1a; 打开91卫图助手&#xff0c;更换底图为高德影像/腾讯影像。(百度影像的地理投影格式有自身加密&#xff…

剖析容器运行时

特别说明&#xff1a;一部分转载自大佬文章&#xff1a;https://blog.csdn.net/weixin_39246554/article/details/120926174&#xff08;不得不说大佬总结的真好啊&#xff01;&#xff01;&#xff01;&#xff09; 剩下的听老王公开课总结。 k8s官网关于运行时的说明&#x…

Typora Mac版本安装Pandoc导出文件为word格式(windows可通用)

我们在用Typora时导出的格式常常为PDF格式&#xff0c;但是如果我们要将文件导出为word格式的时候却需要安装插件PanDoc&#xff0c;我目前使用的是Mac版本的Typora&#xff0c;给大家分享一下如何安装Pandoc以及导出word格式文件。 1.根据Typora中的说明进入GitHub下载Pandoc…

Maven安装配置

Maven安装配置一、下载 apache-maven-3.6.1Maven官网:https://maven.apache.org/download.cgi(或)直接下载maven-3.8.6:https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.zip解压到当前文件夹二、配置 maven 环境变量右键此电脑 - 属性 - 高级…

MySQL学习——执行计划

MySQL中可以通过explain关键字模拟优化器执行SQL语句,从而知道MySQL是如何处理SQL语句的,这将有利我们做代码的优化。 1、MySQL查询执行过程客户端向MySQL服务器发送一条查询请求 服务器首先检查查询缓存,若缓存中存在,则立刻返回存储在缓存中的结果。否则进入下一阶段 服务…

扫码挪车小程序源码专业版上线了

1 、做挪车码之前&#xff0c;先说一些我个人的观点&#xff0c;大家一起探讨学习交流。 2 、挪车码已经是普遍已久的项目&#xff0c;其核心主要在于解决了车主的隐私问题。 3 、观察过目前市面上所有的挪车码系统&#xff0c; 公司也购买了一套测试了完整流程&#xff0c;盈…

【图像分割】基于matlab萤火虫算法图像分割【含Matlab源码 2136期】

一、获取代码方式 获取代码方式1: 完整代码已上传我的资源:【图像分割】基于matlab萤火虫算法图像分割【含Matlab源码 2136期】 点击上面蓝色字体,直接付费下载,即可。 获取代码方式2: 付费专栏图像处理(Matlab) 备注: 点击上面蓝色字体付费专栏图像处理(Matlab),…

parted分区步骤

parted分区步骤概述 通常我们用的比较多的一般都是fdisk工具来进行分区,但是现在由于磁盘越来越廉价,而且磁盘空间越来越大;而fdisk工具他对分区是有大小限制的,它只能划分小于2T的磁盘。但是现在的磁盘空间很多都已经是远远大于2T了,甚至达到2.5T和3T,那要怎么办能,有两…

路径规划总结(一)

第三讲 路径规划 ps:排版有一些问题&#xff0c;懒得改了&#xff0c;见Github 一、导航规划简介 导航规划&#xff1a;在给定环境的全局或局部知识以及一个或者一系列目标位置的条件下&#xff0c;使机器人能够根据知识和传感器感知信息高效可靠地到达目标位置。 导航规划类…

告别传统FTP!该了解一下替代FTP的新产品了

在某些情况下&#xff0c;需要从服务器上传&#xff08;或下载&#xff09;文件。多年来&#xff0c;最流行的文件传输方法是文件传输协议&#xff08;FTP)。FTP的一大优点是它支持断点续传。FTP收获了方便性&#xff0c;却在安全性上有所欠缺。FTP未加密&#xff0c;这意味着格…

Cache-Augmented Inbatch Importance Resampling for Training Recommender Retriever

目录概符号说明启发本文方法BIR (inbatch importance resampling)XIR (Cache-Augmented Resampling)Chen J., Lian D., Li Y., Wang B., Zheng K. and Chen E. Cache-augmented inbatch importance resampling for training recommender retriever. In Advances in Neural Info…

一条sql了解MYSQL的架构设计

1 前言 对于一个服务端开发来说 MYSQL 可能是他使用最熟悉的数据库工具&#xff0c;然而&#xff0c;大部分的Java工程师对MySQL的了解和掌握程度&#xff0c;大致就停留在这么一个阶段:它可以建库、建表、建索引&#xff0c;然后就是对里面的数据进行增删改查&#xff0c;语句…

MacOS/OSX docker修改已运行容器参数的方法

比如我们刚刚docker run了一个容器,然后里面已经配置了一些信息,装了一些东西,然后我发现我忘记了挂载一个文件夹,怎么修改他们呢? 第一个方法: export容器为镜像再import这个镜像 第二个方法: 把现有的容器提交成镜像,然后重新运行. 以上两种方法都相当于你把一台电…