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

Unity热更新

1,热更新的概念与作用


app更新通常分为两类,一种是整包更新(换包),一种是热更新(不换包,通过网络下载,动态更新资源等)。

  • 整包更新,是指在需要更新时,需要用户手动到应用商店或官方网站下载新版本安装包并重新安装的一种更新方式,该方式成本较高,一般只有在无法热更新时才使用。
  • 热更新,是指在不需要重新编译发布应用程序的情况下,通过远程更新服务器向客户端推送程序代码、资源文件等数据的一种技术手段,以修复程序漏洞、优化游戏性能、更新游戏内容等。

热更新又分资源热更新和代码热更新,资源热更新较为简单,一般的app都可实现,而代码热更新,由于考虑到安全性,代码编译等问题,实现起来较为困难,一种实用的方法就是把代码当成资源。Unity热更新就是把代码(如Lua代码)打包成AssetBundle,达到和其它资源一样的更新效果。

在当今快节奏时代,app更新频繁,尤其是游戏app,如果每次更新都需要换包,十分影响用户体验,极易造成用户流失,代价成本实在太高,因此app热更新是十分必要的。


如果有了热更新,会带来什么好处呢?

  • 提高用户体验。热更新可以实现及时修复bug和添加新功能等,减少了玩家等待更新的时间和下载流量,提高了用户的体验感。
  • 降低开发成本。热更新可以在不重新打包的情况下实现游戏的更新,避免了频繁发布新版本的成本和风险。
  • 提高迭代效率。热更新可以快速地进行游戏内容的调整和修改,加快了游戏迭代周期,提高了开发效率。

2,热更新原理

热更新,是通过把最新的资源或代码放到网络服务器,app检测到需要更新版本时,通过网下载资源或代码到本地包,将新的代码或资源加载到应用程序中,以替换旧的代码或资源。

Unity以C#为主要开发语言,如何能做到代码的热更新呢?
C#是编译型语言,Unity在打包后,会将C#编译成一种中间代码IL,后续对这些IL的编译方式不同可以分为AOT和JIT,最终编译为各平台的NativeCode,在没有特殊处理的情况下,无法直接通过替换NativeCode,来达成热更新的。
一种理想化的C#热更新流程是:

  • 把需要更新的代码编译成动态链接库
  • 游戏启动时加载新的动态链接库
  • 用反射的形式获取动态链接库中的实例或方法

这种模式在PC和Android平台是可以的,但在IOS平台是不可行的。因为IOS对申请的内存禁止了可执行权限,所以运行时创建/加载的NativeCode是无法执行的。


为了解决IOS上的热更新问题,有两个主流方案:ILRuntime 和 HybridCLR。


ILRuntime

Unity会把C#代码打包成DLL,ILRuntime在运行时用自己的解释器来解释IL并执行,而不是直接调用.NET FrameWork或Mono虚拟机来运行代码。它借助Mono.Cecil库来读取DLL的PE信息,以及当中类型的所有信息,最终得到方法的IL汇编码,然后通过内置的IL解译执行虚拟机来执行DLL中的代码。
但是ILRuntime会有不少限制

  • ILRuntime和原始的 compiler是两套东西,也就是说你的热更DLL和主工程的DLL实质是不互通的(如热更DLL中一个类要继承主工程DLL的一个类),所以就存在跨域问题,需要写委托适配器,委托转换器。在发布版本后这些不能热更,使用之前一定要预留好可能会使用的
  • 部分 C# 语法不支持:由于 ILRuntime 是基于 Mono 实现的,而 Mono 不支持所有 C# 语法,所以 ILRuntime 在某些 C# 语法方面也有限制,比如属性、泛型委托、可选参数等
  • 需要特殊处理的代码:由于 ILRuntime 的实现方式,一些特殊的代码需要进行特殊处理,比如反射、LINQ、协程等
  • 性能问题:由于 ILRuntime 需要动态解析和执行代码,相对于编译时静态绑定的方式,其性能会有一定程度的下降。同时,在使用过程中也需要注意避免频繁的跨域调用和反射操作,以免影响性能
  • ILRuntime对多线程Thread不兼容,在热更代码里使用多线程会导致Unity崩溃闪退

HybridCLR

是一个特性完整、零成本、高性能、低内存的近乎完美的Unity全平台原生c#热更方案。
IL2CPP是一个纯静态的AOT运行时,不支持运行时加载dll,因此不支持热更新。HybridCLR扩充了IL2CPP的代码,使其由纯AOT Runtime变成“AOT+Interpreter”混合Runtime,进而原生支持动态加载Assembly,使得基于IL2CPP打包的游戏不仅能在Android平台,也能在IOS、Consoles等限制了JIT的平台上高效地以AOT+interpreter混合模式执行。


HybridCLR是近年来一种划时代的Unity原生C#热更新技术,见https://hybridclr.doc.code-philosophy.com/

相比于直接热更新C#代码,使用C#+Lua脚本的热更新方案是目前最主流的实现方式。
Lua是一种跨平台的脚本语言,它主要依赖解释器和虚拟机实现跨平台功能,Lua是解释型语言,并不需要事先编译,而是运行时动态解释执行的。这样Lua就和普通的游戏资源如图片,文本没有区别。由于解释器和虚拟机都是跨平台的,lua脚本也就可以在不同的平台上运行了。
本质上就是利用相关插件(如ulua、slua、tolua、xlua等)提供一个Lua的运行环境(虚拟机),为Unity提供Lua编程的能力,让C#和Lua可以相互调用和访问。

3,xLua热更新方案

xLua是腾讯一个开源项目,xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。
xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的是:

  • 可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
  • 编辑器下无需生成代码,开发更轻量;
  • 出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;

下载地址:https://github.com/Tencent/xLua


4,xLua的简单使用

4.1,xLua安装使用

xLua下载后,将xLua文件中的Assets文件夹下的文件放到项目中的Assets文件下,就完成了XLua的安装。
新建C#代码LuaManager.cs

using UnityEngine;
using XLua;public class LuaManager : MonoBehaviour
{LuaEnv m_luaEnv;void Start(){m_luaEnv = new LuaEnv();m_luaEnv.DoString("print('Hello World')");}
}

新建场景,挂上LuaManager.cs,运行,看到打印 Hello World ,则安装成功了

4.2,自定义Lua加载器

要想执行lua文件,就要用上Lua加载器了,修改LuaManager.cs


using System;
using System.IO;
using UnityEngine;
using XLua;public class LuaManager : MonoBehaviour
{public static string LuaDir = "src"; // 存放lua文件的位置,Assets根目录下LuaEnv m_luaEnv;Action m_startAction;Action m_updateAction;void Start(){m_luaEnv = new LuaEnv();m_luaEnv.AddLoader(new LuaEnv.CustomLoader(this.LuaLoaderFromRes));// 请求执行src下的Main.lua文件m_luaEnv.DoString("require('Main')", "chunk");LuaTable luaTable = this.m_luaEnv.Global.Get<LuaTable>("Main");if (luaTable != null){m_startAction = luaTable.Get<Action>("Start");m_updateAction = luaTable.Get<Action>("Update");}// 执行Main.lua Start方法m_startAction?.Invoke();}void Update(){// 执行Main.lua Update方法m_updateAction?.Invoke();}private byte[] LuaLoaderFromRes(ref string filePath){filePath = filePath.Replace('.', '/');if (!filePath.EndsWith(".lua")){filePath += ".lua";}#if UNITY_EDITORstring path = Application.dataPath + "/" + LuaDir + "/" + filePath;if (File.Exists(path)){//读取路径下的文件的值以字节形式返回return File.ReadAllBytes(path);}
#endif// TODO// android ios 等平台读取lua文件return null;}
}

在Assets目录下新建文件夹src,src文件夹下新建文件Main.lua

Main = {}
setmetatable(Main, {__index = _G})
local _ENV = Mainfunction Start()print("Lua Start")
endfunction Update()-- TODO
endreturn Main

运行,看到打印 Lua Start ,表示成功,Update()可增加每帧的逻辑,可在src下继续增加其它lua文件

4.3,Lua调用C#

[LuaCallCSharp],在C#类加上标签[LuaCallCSharp],就可在Lua中访问了
新建C#代码GameTest.cs

using UnityEngine;
using XLua;namespace MyGame
{[LuaCallCSharp] // 建立Lua调用C#的映射public class GameTest : MonoBehaviour{public string Name;void Start(){Debug.Log("Name:" + Name);}public void CallTest(string text){Debug.Log("Lua Call:" + text);}}
}

修改Main.lua

Main = {}
setmetatable(Main, {__index = _G})
local _ENV = Mainfunction Start()print("Lua Start")-- 访问C#的类,使用CS + 命名空间 + 类名local go = CS.UnityEngine.GameObject("LuaGameObject")local test = go:AddComponent(typeof(CS.MyGame.GameTest))test.Name = "Game Test"-- 调用方法,使用:test:CallTest("666")
endfunction Update()-- TODO
endreturn Main

如果不想在每个类中加标签[LuaCallCSharp],也可以参考XLua/Editor/ExampleConfig,集中配置。
注意,如果需要打包,需提前生成Wrap文件,执行菜单命令:XLua/Generate Code

至于C#调用Lua,4.2代码已有了,更详细的参考官方例子

推荐一个基于xLua的Unity游戏纯lua客户端完整框架:https://github.com/smilehao/xlua-framework

5,xLua可热更规则:

  • 进入lua层后的一切逻辑、资源都可热更
  • app中基本所有的资源(图片、声音、3d模型、动作、特效、文本文件)、lua代码都可热更
  • C#层代码不可热更(也不完全不能,xlua.hotfix可以修改C#代码的执行,替换原来的逻辑,但这是lua代码,不是直接修改C#)
  • 需要新增或修改的代码必须是C#代码则不可热更

6,热更新流程

6.1,更新前准备

  1. 打包AssetBundle,打包程序会比较Unity所有资源,与上次打包后对比实现增量打包,生成md5信息文件(assetbundlemd5.txt),版本信息文件(version.txt),递增资源版本号
  2. 上传AssetBundle,assetbundlemd5.txt,version.txt到网络服务器(cdn)
  3. 停服或后台通知用户在线更新

6.2,更新流程

  1. 启动app,下载版本信息文件version.txt
  2. 版本号的比较,如果版本号不同才继续以下流程
  3. 下载资源服务器上的md5对比文件(assetbundlemd5.txt)
  4. 确定下载列表,将最新下载的md5对比文件和本地旧md5对比文件对比,记录缺少或不同的文件。(assetbundlemd5.txt中的md5码实现此步骤)
  5. 根据下载列表,下载所需的资源。(一般放在Application.persistentDataPath)
  6. 保证下载成功后,用最新的md5对比文件覆盖本地的md5对比文件(更新assetbundlemd5.txt),记录最新的版本号

7,Unity热更新实现

版本信息文件version.txt

{"code":0,"data":{"isUpdateClient":0,"isUpdateRes":1,"version":"1.0","resVersion":"1.0.0.1","clientUrl":"https://aa.bb.cc.com/game/client.apk","resUrl":"https://aa.bb.cc.com/game/res/"}
}

这是version.txt的结构参考,JSON格式,字段说明:

  • isUpdateClient:是否强制更新整包,视情况是否打开
  • isUpdateRes:是否更新资源开头,无特殊情况都是打开
  • version:客户端版本号,如果此版本号对比不一样,需考虑更新整包
  • resVersion:资源版本号,如果此版本号对比不一样,则进行热更新,每次打包递增
  • clientUrl:客户端整包的更新地址,可根据后缀,跳转网站,或直接下载安装
  • resUrl:热更新资源的地址

App启动,下载version.txt,版本比较代码

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;public class GameStart : MonoBehaviour
{public string VersionUrl = "https://aa.bb.cc.com/game/version.txt"; // version.txt网络服务器地址public string appVersion = "1.0"; // 当前客户端版本号public string currentResVersion = "1.0.0.1"; // 当前最新资源版本号void Start(){// app 启动前逻辑,如读取客户端版本号,最新资源版本号//currentResVersion = PlayerPrefs.GetString("currentResVersion");StartCoroutine(RequestVersionInfo());}IEnumerator RequestVersionInfo(){// 加上时间戳,确保下载的是最新文件UnityWebRequest request = new UnityWebRequest(VersionUrl + "?time=" + System.DateTime.Now.Ticks);request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();yield return request.SendWebRequest();if (request.error == null){string text = request.downloadHandler.text;LitJson.JsonData versionInfo = LitJson.JsonMapper.ToObject(text);int isUpdateClient = (int)versionInfo["data"]["isUpdateClient"];int isUpdateRes = (int)versionInfo["data"]["isUpdateRes"];string version = (string)versionInfo["data"]["version"];string clientUrl = (string)versionInfo["data"]["clientUrl"];string resUrl = (string)versionInfo["data"]["resUrl"];string resVersion = (string)versionInfo["data"]["resVersion"];if (isUpdateClient == 1){if (compareResVersion(version, appVersion)){// 提示客户端更新// Application.OpenURL(clientUrl);}}if (isUpdateRes == 1){if (compareResVersion(resVersion, currentResVersion)){// 进入热更新;//StartHotUpdate(resUrl);}}}request.Dispose();}public bool compareResVersion(string resVersion1, string resVersion2){var arr1 = resVersion1.Split('.');var arr2 = resVersion2.Split('.');for (int i = 0; i < arr1.Length; i++){if (int.Parse(arr1[i]) > int.Parse(arr2[i])){return true;}}return false;}}

热更新流程代码

IEnumerator StartHotUpdate(string resUrl)
{bool downloadFailed = false;// 下载网络服务器最新md5信息文件string md5Url = resUrl + "assetbundlemd5.txt";UnityWebRequest md5Request = new UnityWebRequest(md5Url + "?version=" + currentResVersion); // 加上版本号,确保下载的是最新文件md5Request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();yield return md5Request.SendWebRequest();if (md5Request.error == null){AssetBundleMD5Infos remoteMd5_info = new AssetBundleMD5Infos(md5Request.downloadHandler.data); // 网络服务器最新md5信息文件AssetBundleMD5Infos tmpMd5_info; // 由于出错中断暂时保存的md5信息文件string dirPath = Application.persistentDataPath + "/" + Utility.GetPlatformName();if (!Directory.Exists(dirPath)){Directory.CreateDirectory(dirPath);}dirPath = dirPath + "/";if (File.Exists(dirPath + "assetbundlemd5.tmp")){byte[] fileContent = File.ReadAllBytes(dirPath + "assetbundlemd5.tmp");tmpMd5_info = new AssetBundleMD5Infos(fileContent);}else{tmpMd5_info = new AssetBundleMD5Infos(null);}List<string> needUpdateAbs = new List<string>(); // 需要下载更新的ab文件列表foreach (var abName in remoteMd5_info.m_AssetBundleMD5.Keys){string remoteMd5 = remoteMd5_info.GetAssetBundleMD5(abName);// 与网络服务器最新md5比较,不同则加载下载更新列表,AssetBundleManager.GetAssetBundleMD5(abName)本地最新md5if (tmpMd5_info.GetAssetBundleMD5(abName) != remoteMd5 && remoteMd5 != AssetBundleManager.GetAssetBundleMD5(abName)){needUpdateAbs.Add(abName);}}// 下载更新的ab文件foreach (string abName in needUpdateAbs){UnityWebRequest abRequest = new UnityWebRequest(resUrl + abName + "?version=" + currentResVersion); // 加上版本号,确保下载的是最新文件abRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();yield return abRequest.SendWebRequest();if (abRequest.error == null){// 保存到最新的ab文件到本地File.WriteAllBytes(dirPath + abName, abRequest.downloadHandler.data);tmpMd5_info.AddAssetBundleMD5(abName, remoteMd5_info.GetAssetBundleMD5(abName), remoteMd5_info.GetAssetBundleSize(abName), remoteMd5_info.GetAssetBundleMiniGameId(abName));} else{downloadFailed = true;}abRequest.Dispose();}if (needUpdateAbs.Count > 0){if (!downloadFailed){// 保存最新的md5文件remoteMd5_info.SerializeToFile(dirPath + "assetbundlemd5.txt");File.Delete(dirPath + "assetbundlemd5.tmp");}else{tmpMd5_info.SerializeToFile(dirPath + "assetbundlemd5.tmp"); // 出错中断保存临时的md5,避免下次更新重新下载}}}else{downloadFailed = true;}md5Request.Dispose();if (downloadFailed){// 出错重新执行更新流程StartCoroutine(StartHotUpdate(resUrl));}
}

相关文章:

Unity热更新

1&#xff0c;热更新的概念与作用 app更新通常分为两类&#xff0c;一种是整包更新&#xff08;换包&#xff09;&#xff0c;一种是热更新&#xff08;不换包&#xff0c;通过网络下载&#xff0c;动态更新资源等&#xff09;。 整包更新&#xff0c;是指在需要更新时&#x…...

如何用维格云搭建和一键训练你的钧瓷AI机器人?

大禹智库 第69期(总第400期) 2023年11月4日 如何用维格云搭建和一键训练你的钧瓷AI机器人? 钧瓷私有数据聊天机器人是一种能够根据预设的数据集进行智能对话的机器人。通过维格云,我们可以轻松地搭建自己的钧瓷私有数据聊天机器人。本文将以钧道机器人为例,详细介绍如何…...

整理的一些Java细节问题

1. 为什么要有无参构造&#xff1f; 在 Java 中&#xff0c;如果一个类没有显式定义构造方法&#xff0c;编译器会自动生成一个默认的无参构造方法&#xff08;也称为默认构造方法&#xff09;。无参构造方法是一个没有任何参数的构造方法。 无参构造方法的存在有几个重要原因…...

初识AUTOSAR网络管理

文章目录 目的模式时间参数T_REPEAT_MESSAGET_NM_TIMEOUTT_WAIT_BUS_SLEEPT_START_Tx_AppFrameT_NM_ImmediateCycleTimeT_NM_MessageCycleN_ImmediateNM_TIMEST_START_NM_TXT_WakeUp跳转状态NM_1NM_2NM_3NM_4NM_5NM_6NM_7...

Flink SQL Hive Connector使用场景

目录 1.介绍 2.使用 2.1注册HiveCatalog 2.2Hive Read 2.2.1流读关键配置 2.2.2示例...

【Docker】联合探讨Docker:容器化技术的革命性应用

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 &#x1f4d5;作者简介&#xff1a;热…...

dirhunt使用手册,中文版

“dirhunt” 的命令行工具的帮助信息&#xff0c;用于目录扫描和网站内容分析。以下是这个命令的使用方法和示例&#xff1a; 命令格式&#xff1a; dirhunt [OPTIONS] [URLS]… [URLS]…&#xff1a;一个或多个域名或 URL&#xff0c;可以加载来自文件的 URL&#xff0c;使用…...

【从0到1设计一个网关】如何设计一个稳定的网关?

文章目录 高可用分析软件架构心跳检测自动恢复熔断降级接口重试隔离压测和预案多机房灾备以及双活数据中心异常处理机制重试主备服务自动切换动态剔除或恢复异常机器超时时间的考虑服务设计这篇文章并没有具体的业务实现,而只是对于如何设计一个高可用,稳定的网关列举出了一些…...

chromedp库编写程序

步骤1&#xff1a;首先&#xff0c;我们需要导入chromedp库&#xff0c;以便使用它来下载网页内容。 import chromedp 步骤2&#xff1a;然后&#xff0c;我们需要创建一个函数&#xff0c;该函数接受一个URL作为参数&#xff0c;并使用chromedp库下载该URL的内容。 func do…...

pngquant failed to build, make sure that libpng-dev is installed 问题

第一个参考方案失败 &#xff1a;npm install -g windows-build-tools4.0.0 安装失败&#xff0c;提示 依赖不在支持 第二个方案&#xff0c;降低node 版本 失败 第三种方案&#xff0c;成功 先执行&#xff0c;下面两行代码&#xff0c;再按照依赖 npm install imagemin-pn…...

进程控制(二):进程等待

文章目录 进程控制&#xff08;二&#xff09;进程等待wait函数waitpid函数wait/waitpid获取子进程状态码的过程进程等待相关的宏 总结 进程控制&#xff08;二&#xff09; 延续对于上文进程结束&#xff0c;我们继续对于进程控制进行学习&#xff0c;本文我们主要是对于进程…...

SWAT-MODFLOW地表水与地下水耦合模型的建模及应用

目录 第一讲 模型原理与层次结构 第二讲 QGIS软件 第三讲 基于QSWATMOD的SWAT-MODFLOW模拟 第四讲 QSWAT模型介绍与建模 第五讲 基于QGIS的数据制备 第六讲 基于CUP的SWAT参数率定 第七讲 MODFLOW模型讲解 第八讲 结果分析 更多应用 耦合模型被应用到很多科学和工程领…...

使用navicat操纵数据库

<1>连接数据库 打开Navicat&#xff0c;点击“连接”&#xff0c;选择“MySQL”&#xff0c;这边是本机安装的mysql,主机为localhost&#xff0c;输入root密码。 使用Navicat创建数据库并导入SQL文件 SQL查询 普通SQL查询 USE demo; SELECT * FROM t_emp;SELECT emp…...

websocket入门

一&#xff0c;什么是websocket WebSocket是HTML5下一种新的协议&#xff08;websocket协议本质上是一个基于tcp的协议&#xff09;它实现了浏览器与服务器全双工通信&#xff0c;能更好的节省服务器资源和带宽并达到实时通讯的目的Websocket是一个持久化的协议。 WebSocket有…...

Word里MathType插件符号表消失了

场景再现 在word里面编辑数学公式&#xff0c;结果符号表跑到屏幕外面了&#xff0c;找不到&#xff1b; 解决办法 在其中找到视图->工具栏&#xff0c;点击即可&#xff1a; 还嫌弃它跑到外边了&#xff0c;那就可以点击符号表的边框&#xff1a; 双击左边边框&#x…...

利用MySQL玩转数据分析之基础篇

知识无底&#xff0c;学海无涯&#xff0c;到今天进入MySQL的学习4天了&#xff0c;知识点虽然简单&#xff0c;但是比较多&#xff0c;所以写一篇博客将MySQL的基础写出来&#xff0c;方便自己以后查找&#xff0c;还有就是分享给大家。 1、SQL简述 1&#xff09;SQL的概述 S…...

【ML】分类问题

分类问题 classification&#xff1a;根据已知样本特征&#xff0c;判断输入样本属于哪种已知样本类。 常用入门案例&#xff1a;垃圾邮件检测、图像分类、手写数字识别、考试通过预测。 分类问题和回归问题的明显区别&#xff1a; 分类问题的结果是非连续型标签&#xff0c…...

python @classmethod装饰器作用 与 使用 类方法 实例方法

1 表示是类方法, 类方法可以修改类变量, 实例方法不能修改类变量 类方法可以访问和修改类变量&#xff08;也称为类属性&#xff09;。这是实例方法做不到的&#xff0c;因为实例方法只能访问和修改实例变量&#xff08;也称为实例属性&#xff09; 1.1 例子 class MyClass:…...

layui form 中input输入框长度的统一设置

Layui.form中使用class"layui-input-inline"就可轻松将元素都放到一行&#xff0c;但如果元素过多&#xff0c;就会自动换行。那就需要手动设置input框的长度。 像这种情况&#xff1a; 其实只需要添加css样式就可修改了 .layui-form-item .layui-input-inline {wid…...

【WSL/WSL 2-Redis】解决Windows无法安装WSL Ubuntu子系统与Redis安装

前言 在现代计算环境中&#xff0c;开发人员和技术爱好者通常需要在不同的操作系统之间切换&#xff0c;以便利用各种工具和应用程序。在这方面&#xff0c;Windows用户可能发现WSL&#xff08;Windows Subsystem for Linux&#xff09;是一个强大的工具&#xff0c;它允许他们…...

数据结构(四)--队列及面试常考的算法

一、队列介绍 1、定义 与栈相似&#xff0c;队列是另一种顺序存储元素的线性数据结构。栈与队列的最大差别在于栈是LIFO&#xff08;后进先出&#xff09;&#xff0c;而队列是FIFO&#xff0c;即先进先出。 2、优缺点及使用场景 优点&#xff1a;先进先出&#xff08;FIFO&…...

PMIC、电源管理MAX77646ANP、MAX77647AANP、MAX77675AEWE、MAX77847AEWL DC-DC 开关稳压器

一、MAX77646ANP、MAX77647AANP 低IQ SIMO PMIC支持原电池应用的1.8V工作电压 MAX77646/MAX77647为尺寸和效率至关重要的低功耗应用提供电源解决方案。该IC集成单电感多输出(SIMO)降压/升压稳压器&#xff0c;可通过单个电感提供三个可独立编程的电源轨&#xff0c;尽可能地减…...

5W2H分析法:全面思考和解决问题的实用工具

5W2H分析法又叫七问分析法&#xff0c;创于二战中美国陆军兵器修理部。发明者用五个以W开头的英语单词和两个以H开头的英语单词进行设问&#xff0c;发现解决问题的线索&#xff0c;寻找发明思路&#xff0c;进行设计构思&#xff0c;从而搞出新的发明项目。5W2H简单、方便&…...

01 向量基本概念

向量基本概念 向量是什么物理专业学生视角计算机专业学生视角数学家视角 不同视角之间的关系 这是关于3Blue1Brown "线性代数的本质"的学习笔记。 向量是什么 物理专业学生视角 向量是空间中的箭头。向量的长度和方向确定一个向量。只要长度和方向相同&#xff0c…...

QMS质量检验管理|攻克制造企业质量检验难题,助力企业提质增效

在日益激烈的市场竞争中&#xff0c;对产品质量严格把关&#xff0c;是制造企业提高核心竞争力与品牌价值的关键因素。那如何高效、高质地完成产品质检工作&#xff1f;这就需要企业在工业质检中引进数字化技术加以辅助&#xff0c;进而推动智能制造高质量发展。 蓝库云QMS质量…...

Visual Components Robotics OLP解决方案 北京衡祖

Visual Components 引入了“Visual Components Robotics OLP”的重大升级&#xff0c;合并了制造模拟和机器人离线编程。该解决方案利用 Delfoi Robotics 的技术&#xff0c;提高生产率、减少停机时间并减少浪费。 一、探索下一代离线机器人编程软件 自 1999 年以来&#xff0…...

React——简便获取经纬度信息

引言 在现代的Web应用程序中&#xff0c;获取用户的地理位置信息是一项常见的需求。通过获取经纬度信息&#xff0c;我们可以为用户提供个性化的服务和定位功能。在本文中&#xff0c;我们将介绍如何在React应用程序中简便地获取用户的经纬度信息&#xff0c;并提供相应的代码…...

如何修改设置360浏览器内核模式

360安全浏览器现有两种内核模式&#xff0c;即“极速模式”和“兼容模式” 极速模式 “极速模式”是以Blink&#xff08;Webkit&#xff09;为内核的浏览模式&#xff0c;Blink内核具有更高的网页浏览速度和更好网页渲染效果。但由于少部分网银、政府、税务、办公系统等网站对B…...

spring boot 定时任务@Scheduled(cron = ““)不可用时并且注入失败时——笔记

以下方案是本人使用定时任务时Service注入失败的解决方案 在 Spring Boot 中执行定时任务时&#xff0c;你可以注入并直接调用 Service 中的方法&#xff0c;就像在普通的业务逻辑中一样。 以下是执行定时任务时调用 Service 的步骤&#xff1a; 创建一个 Service 类&#xf…...

R语言用jsonlite库写的一个图片爬虫

以下是一个使用R语言和jsonlite库下载图片的程序。首先&#xff0c;我们需要导入jsonlite库和options()函数&#xff0c;然后将代理服务器的主机名和端口号设置为"duoip"和"8000"。接着&#xff0c;我们将URL设置为"https://yun.baidu.com/"&…...