Android-如何实现Apng动画播放
01
Apng是什么
Apng(Animated Portable Network Graphics)顾名思义是基于 PNG 格式扩展的一种动画格式,增加了对动画图像的支持,同时加入了 24 位图像和8位 Alpha 透明度的支持,并且向下兼容 PNG。
Google封面图
02
Apng、Gif对比
1、图片质量
Gif最多支持8位颜色深度,图片边缘锯齿比较严重,Apng支持24位颜色深度色,抗锯齿效果更好。
大象Gif、Apng图
2、透明度
Gif支持全透明或者不透明,不支持透明度渐变,而Apng支持8位Alpha透明通道。

3、图片体积
GIF采用LZW压缩算法,而Apng采用Deflate压缩算法,在相同功能情况下如8位颜色深度,Apng文件体积会更小。
时钟、人物图
4、应用场景
Gif兼容性高,几乎所有浏览器都支持,移动端播放Gif动画控件都比较成熟。Apng兼容性较差,目前支持Apng的浏览器有 Chrome、Firefox、Safari 和 Opera,下面对Gif和Apng的应用场景做下说明:
1)图片质量要求不高的动画场景可以考虑使用Gif,图片质量要求比较高的动画场景可以考虑使用Apng,如动态表情等可以使用Gif,直播间礼物动效可以使用Apng;
2)对于有透明度渐变要求的动画场景,如要加载一个白天变黑夜的动画,使用Apng比较好,如果使用Gif,则会出现白色锯齿毛边;
3)对于像素点变化比较少的动画场景,使用Apng比较好,因为Apng前后像素相似点可以复用,文件体积相对比较小,如下面的城市夜景图:
城市夜景图
03
Android如何实现Apng动画播放

1、Apng格式介绍
Apng的文件后缀名是.png,一张Apng图片包含一系列的png帧,每个png帧又包含当前png的相关信息,其结构图(图片来自维基百科,请点击查看)如下:
以下对上面的结构图进行说明:
1)Apng的IHDR(Image Head Chunk)用来包含Apng图片的宽、高等信息,acTL(Animation Control Chunk)包含动画播放的控制信息;
2)每一帧的fcTL(Frame Control Chunk)包含帧的一些控制信息,如Apng总的帧数,循环播放的次数,fdAT(Frame Data Chunk)包含每帧的图片数据信息,这跟png图片的IDAT块格式保持一致;
3)Apng的第一帧比较特殊,如果Apng的第一帧作为Apng默认显示的图片,则Apng结构跟上图的保持一致。如果Apng的第一帧不作为默认显示的图片,则Apng的默认显示图片从IDAT块取得,而Apng的结构图如下:
Apng结构图(第一帧非默认图)
Apng与正常的png数据结构差不多是一样的,只不过是多了三个chunk类型,分别是:acTL(Animation Control Chunk)、fdAT(Frame Data Chunk)、fcTL(Frame Control Chunk),chunk的格式一般如下:

Chunk由四部分组成,分别数据长度(4个字节),Chunk类型(4个字节),ChunkData,CRC(循环冗余校验,4个字节),下面分别对这三种chunk类型做具体说明。
acTL
acTL是Apng的动画控制Chunk,包含总的帧数num_frames,播放次数num_plays,num_frames和num_plays都占有4个字节,结构如下:
byte 含义
0 num_frames:0~3字节表示该Apng总的播放帧数。
4 num_plays:4~7字节表示该Apng循环播放的次数。
num_frames一定大于等于1,如果等于1,表示Apng只有一帧,可以看成就是一张png图。num_plays一定大于等于0,如果等于0,表示Apng动画是循环播放的。
备注:acTL一定是在IDAT前面。
fdAT
fdAT是每帧的数据Chunk,与png的IDAT结构保持一致,只是额外增加了一个序列号,fdAT的结构如下:
byte 含义
0 sequence_number:0~3字节表示动画帧的编号,从0开始。
4 frame_data :帧的数据信息。
这里需要说明的是,Apng的每一帧至少包含一个fdAT,而每一个fdAT是一张png的像素压缩数据,从该fdAT中,我们可以读取数据获得一张png图片。
fcTL
fcTL是每帧的控制信息块,fcTL一定是出现在fdAT或者IDAT的前面(第一帧为默认图片),fcTL的结构如下:
byte 含义
0 sequence_number:控制帧的序号,从0开始。
4 width:帧的宽度。
8 height:帧的高度。
12 x_offset:在x方向的偏移。
16 y_offset:在y方向的偏移。
20 delay_num:帧动画时间间隙的分子
22 delay_den:帧动画时间间隙的分母
24 dispose_op:在显示该帧之前,需要对前面缓冲输出区域做何种处理。
25 blend_op:具体显示该帧的方式。
下面对fcTL控制块包含的内容做具体说明:
1)x_offset,y_offset表示显示当前帧时,x方向和y方向需要偏移的距离,如果第一帧为默认显示图片,则x_offset,y_offset为0;
2)width、height、x_offset、y_offset、Apng的宽高(包含在IHDR块中)之间存在下面的约束条件:
x_offset >= 0
y_offset >= 0
width > 0
height > 0
x_offset + width<= IHDR width
y_offset + height <= IHDR height
3)Apng动画每帧的显示时长可以通过delay_num * DELAY_FACTOR / delay_den
来获得,时间单位是ms,其中DELAY_FACTOR是一个常量,为1000,下面对delay_num、delay_den为0的情况做下说明。delay_num = 0 :得到帧动画时间间隙为0,当前帧播放完了,立即播放下一帧。delay_den = 0:delay_den为0,默认把其当做100来计算帧动画时间间隙,计算公式是:delay_num * DELAY_FACTOR / 100
。
4)dispose_op有三种类型,如下:
value 含义
0 APNG_DISPOSE_OP_NONE:不做任何处理。
1 APNG_DISPOSE_OP_BACKGROUND:前一帧的x方向的偏移、y方向的偏移和当前帧的宽、高做一个剪裁,并将剪裁的区域抠成全黑色透明。
2 APNG_DISPOSE_OP_PREVIOUS:将当前缓冲输出区域恢复到先前的内容区域。
5)blend_op有两种类型,如下:
value 含义
0 APNG_BLEND_OP_SOURCE:通过dispose处理后得到的bitmap,在该bitmap对当前帧的x方向的偏移、y方向的偏移和当前帧的宽、高做一个剪裁,并将剪裁的区域抠成全黑色透明,最后将当前帧写到该剪裁区域上。
1 APNG_BLEND_OP_OVER:当前帧覆盖到当前的缓冲区域并显示。
2、Apng动画播放整体设计
Apng动画播放流程图
Apng动画播放实现分为如下几个步骤:
step1:通过MappedByteBuffer加载Apng文件,读取Apng的acTL(控制帧),保存播放次数等信息;
step2:使用SurfaceView容器,在子线程中从内存中读取fdAT数据帧)、fcTL(数据控制帧),假设第N帧,读取完后经过消除、合成处理,最后在SurfaceView的Canvas进行绘制;
step3:不停地重复步骤2的操作,直至Apng动画循环播放结束。
3、Apng动画播放详细设计
Apng动画播放流程包括Apng解析和Apng渲染两个过程,Apng解析主要有两种方法,下面我们将会介绍,而Apng渲染主要包括三个步骤:消除(dispose)、合成(blend)、绘制(draw),由此得到Apng动画播放流程图如下:
Apng动画播放流程
消除dispose:指定了下一帧绘制之前对缓冲区的操作
1)不清空画布,直接把新的图像数据渲染到画布指定的区域
2)在渲染下一帧前将当前帧的区域内的画布清空为默认背景色
3)在渲染下一帧前将画布的当前帧区域内恢复为上一帧绘制后的结果
合成blend:指定了绘制当前帧之前对缓冲区的操作
1)表示清除当前区域再绘制
2)表示不清除直接绘制当前区域,图像叠加
渲染:生成Bitmap图片并在画布上进行绘制
Apng的解析
Apng的解析主要是将Apng文件转化成Apng序列帧Frame-n,从上面的流程图可知,Apng文件的解析列出了两种方案,下面来分别说说:
1)Apng文件首先经过一个解压(ApngExact)的过程,生成png序列帧保存在本地,然后经过加载(LoadPng)处理生成序列帧Frame-n。假设Apng动画文件总共有90帧,那么经过ApngExact处理后,会生成90张png序列帧保存在本地,每帧通过LoadPng处理生成Bitmap并供后面的Apng渲染使用。
2)Apng是一个独立的文件,我们自己编写读取Apng文件的代码类:ApngReader,当渲染第i帧时,通过ApngReader直接获取第i帧的Bitmap。
比较:
1)方案一是将Apng文件全部解压成png序列图片保存在本地,方案二是把Apng文件当做一个整体去处理,需要第几帧直接读取第几帧,并将该帧以Bitmap的形似保存到内存。
2)方案一解压得到的png图片在后面的渲染中需要转化成Bitamp,而方案二直接就获取了第几帧的Bitmap,相比于方案一,方案二减少了一个从SD卡读取png文件的操作。
Apng的渲染
方案一的具体实现大家可以参考github上面的一个项目apng-view,下面我们来讲讲方案二的具体实现,即ApngReader的具体实现。
step1:解析Apng的每一帧
我们是将整个文件放到一个buffer里面,并且通过RandomAccessFile、MappedByteBuffer来读取Apng的每一帧,ApngReader的构造函数如下:
public ApngReader(String apngFile) throws IOException, FormatNotSupportException {RandomAccessFile f = new RandomAccessFile(apngFile, "r");mBuffer = f.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f.length());f.close();if (mBuffer.getInt() != PNG_SIG&& mBuffer.getInt(4) != PNG_SIG_VER&& mBuffer.getInt(8) != CODE_IHDR) {throw new FormatNotSupportException("Not a png/apng file");}mChunk = new ApngMmapParserChunk(mBuffer);reset();}
下面来看看读取每一帧的方法:
/*** get next frame control info & bitmap** @return next frame control info, or null if no next FCTL chunk || no next IDAT/FDAT* @throws IOException*/public ApngFrame nextFrame() throws IOException {// reset read pointers from previous frame's lockmPngStream.clearDataChunks();mPngStream.resetPos();mChunk.unlockRead();// locate next FCTL chunkboolean ihdrCopied = false;while (mChunk.typeCode != CODE_fcTL) {switch (mChunk.typeCode) {case CODE_IEND:return null;case CODE_IHDR:mPngStream.setIHDR(mChunk.duplicateData());break;case CODE_acTL:handleACTL(mChunk);ihdrCopied = true;break;default:handleOtherChunk(mChunk);}mChunk.parseNext();}// located at FCTL chunkApngFrame frame = new ApngFrame();mChunk.assignTo(frame);// locate next IDAT or fdAt chunkmChunk.parseNext();// first move next from current FCTLwhile (mChunk.typeCode != CODE_IDAT && mChunk.typeCode != CODE_fdAT) {switch (mChunk.typeCode) {case CODE_IEND:return null;case CODE_IHDR:mPngStream.setIHDR(mChunk.duplicateData());ihdrCopied = true;break;case CODE_acTL:handleACTL(mChunk);break;default:handleOtherChunk(mChunk);}mChunk.parseNext();}// located at first IDAT or fdAT chunk// collect all consecutive dat chunksboolean needUpdateIHDR = true;int dataOffset = mChunk.getOffset();while (mChunk.typeCode == CODE_fdAT || mChunk.typeCode == CODE_IDAT) {if (needUpdateIHDR && (!ihdrCopied || mChunk.typeCode == CODE_fdAT)) {mPngStream.updateIHDR(frame.getWidth(), frame.getHeight());needUpdateIHDR = false;}if (mChunk.typeCode == CODE_fdAT) {mPngStream.addDataChunk(new Fdat2IdatChunk(mChunk));} else {mPngStream.addDataChunk(new ApngMmapParserChunk(mChunk));}mChunk.parseNext();}// lock position for this frame's image as OutputStreammChunk.lockRead(dataOffset);frame.imageStream = mPngStream;return frame;}
step2:Apng的消除操作
Apng的消除操作是在ApngFrameRender的render方法做的,方法如下:
/*** 渲染当前帧画面** @param frame apng中当前帧* @return 渲染合成后的当前帧图像*/public Bitmap render(ApngFrame frame, Bitmap frameBmp) {// 执行消除操作dispose(frame);// 合成当前帧blend(frame, frameBmp);return mRenderFrame;}
dispose(ApngFrame frame)方法如下:
/*** 帧图像析构消除 - 提交结果*/private void dispose(ApngFrame frame) {// last frame dispose opswitch (mLastDisposeOp) {case APNG_DISPOSE_OP_NONE:// no opbreak;case APNG_DISPOSE_OP_BACKGROUND:// clear rectmRenderCanvas.clipRect(mDisposeRect);mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);mRenderCanvas.clipRect(mFullRect, Region.Op.REPLACE);break;case APNG_DISPOSE_OP_PREVIOUS:// swap work and cache bitmapBitmap bmp = mRenderFrame;mRenderFrame = mDisposedFrame;mDisposedFrame = bmp;mRenderCanvas.setBitmap(mRenderFrame);mDisposeCanvas.setBitmap(mDisposedFrame);break;}// current frame dispose opmLastDisposeOp = frame.getDisposeOp();switch (mLastDisposeOp) {case APNG_DISPOSE_OP_NONE:// no opbreak;case APNG_DISPOSE_OP_BACKGROUND:// cache rect for next clear disposeint x = frame.getxOff();int y = frame.getyOff();mDisposeRect.set(x, y, x + frame.getWidth(), y + frame.getHeight());break;case APNG_DISPOSE_OP_PREVIOUS:// cache bmp for next restore disposemDisposeCanvas.clipRect(mFullRect, Region.Op.REPLACE);mDisposeCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);mDisposeCanvas.drawBitmap(mRenderFrame, 0, 0, null);break;}}
step3:Apng的合成操作
Apng的合成操作是在每一帧经过dispose之后做的,具体方法是blend(ApngFrame frame, Bitmap frameBmp),代码如下:
/*** 帧图像合成*/private void blend(ApngFrame frame, Bitmap frameBmp) {int xOff = frame.getxOff();int yOff = frame.getyOff();mRenderCanvas.clipRect(xOff, yOff, xOff + frame.getWidth(), yOff + frame.getHeight());if (frame.getBlendOp() == APNG_BLEND_OP_SOURCE) {mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);}mRenderCanvas.drawBitmap(frameBmp, xOff, yOff, null);mRenderCanvas.clipRect(mFullRect, Region.Op.REPLACE);}
step4:Apng的绘制
Apng的每一帧经过消除、合成操作之后,就可以在View上面draw,具体代码如下:
/*** draw the appointed frame*/private void drawFrame(AnimParams animItem, ApngFrame frame, Bitmap frameBmp) {if (surfaceEnabled && !isInterrupted()) {//start to draw the frametry {Matrix matrix = new Matrix();matrix.setScale(mScale, mScale);Bitmap bmp = mFrameRender.render(frame, frameBmp);//saveBitmap(bmp, index);index ++;Canvas canvas = getHolder().lockCanvas();//anti-aliasingcanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);float[] tranLeftAndTop = ApngUtils.getTranLeftAndTop(canvas, bmp, animItem.align, mScale, animItem.percent);canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));matrix.postTranslate(tranLeftAndTop[0], tranLeftAndTop[1]);canvas.drawBitmap(bmp, matrix, null);getHolder().unlockCanvasAndPost(canvas); // unlock the canvas} catch (Exception e) {Log.e(TAG, "draw error msg:" + Log.getStackTraceString(e));}}}
04
最后
要完成Apng的播放,就必须实现Apng解析和渲染两个步骤,实现Apng解析后,在Android控件的Canvas不断绘制png图片就实现png图片的连续播放,达到动图的播放效果,该方案比传统的Apng播放实现有如下两个优点:
1)Apng的每一帧图片都是从内存加载,不是从磁盘加载,无IO操作,动画播放不会卡顿;
2)Apng的每一帧渲染是在子线程里面完成的,不会阻塞UI主线程,不会出现ANR。
除此之外,Apng相比Gif,能够在提高动画质量的同时,较容易实现动画Alpha渐变效果。
如果你想了解更多Apng,如Apng的制作,Apng对比WebP等相关知识,可以参见Apng。
相关文章:
Android-如何实现Apng动画播放
01 Apng是什么 Apng(Animated Portable Network Graphics)顾名思义是基于 PNG 格式扩展的一种动画格式,增加了对动画图像的支持,同时加入了 24 位图像和8位 Alpha 透明度的支持,并且向下兼容 PNG。 Google封面图 02 A…...
微服务系统架构图
微服务架构是一种将单一应用程序开发为一组小型服务的架构风格。每个服务都在自己的进程中运行,它们之间采用轻量级的通信机制(如 HTTP/REST 或消息队列)进行相互协作。以下是关于微服务系统架构的简要介绍:一、核心特点独立部署 …...
04 —— Webpack打包CSS代码
加载器css-loader :解析css代码 webpack 中文文档 | webpack中文文档 | webpack中文网 加载器style-loader:把解析后的css代码插入到DOM style-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 准备css代码,放到src/login目…...
【数据结构专栏】二叉搜索树(Binary Search Tree)的剖析?
文章目录 🧨前言1、二叉搜索树的基本概念?2、二叉搜索树的节点结构组成?3、二叉搜索树的插入操作?4、二叉搜索树的删除操作?5、二叉搜索树的遍历? 6、二叉搜索树的性能分析? 🎉完整代…...
SSM全家桶 1.Maven
或许总要彻彻底底地绝望一次 才能重新再活一次 —— 24.11.20 maven在如今的idea中已经实现自动配置,不需要我们手动下载 一、Maven的简介和快速入门 Maven 是一款为 Java 项目构建管理、依赖管理的工具(软件),使用 Maven 可以自动化构建测试、打包和发…...
前端三剑客(二):CSS
目录 1. CSS 基础 1.1 什么是 CSS 1.2 语法格式 1.3 引入方式 1.3.1 行内样式 1.3.2 内部样式 1.3.3 外部样式 1.4 CSS 编码规范 2. 选择器 2.1 标签选择器 2.2 id 选择器 2.3 class 选择器(类选择器) 2.4 复合选择器 2.5 通配符选择器 3. 常用 CSS 样式 3.1 c…...
解析html将表格插入word文档
用到依赖包 <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.2</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweav…...
Java核心知识详解:String类、StringBuffer、数组及日期时间的全面解析
🚀 作者 :“码上有前” 🚀 文章简介 :Java 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 标题 Java核心知识详解:String类、StringBuffer、数组及日期时间的全面解析 摘要 在Java中…...
唯一设备序列号(UID)、唯一产品识别码(UPI)和备份寄存器BKP
文章目录 一、唯一设备序列号UID二、唯一产品识别码UPI三、备份寄存器BKP 一、唯一设备序列号UID MCU 系列产品内置两个不同长度的唯一设备序列号,分别为 96 位的 UID(Unique device ID)和 128 位的UCID(Unique Customer ID),这两个设备序列号存放在闪存…...
Kafka - 消费者程序仅消费一半分区消息的问题
1. 问题描述 修改安全服务状态有时逻辑正常有时候逻辑不正常,排查incident服务的日志发现消息可以正常发送到 kafka topic ,但是incident-cron 服务有时候有拉取消息的日志有时候没有日志。 kafka 生产者可以将消息正常发送到 kafka topic ,…...
养老院管理系统+小程序项目需求分析文档
智慧综合养老服务平台是以业务为牵引、场景为驱动,围绕“老人”业务域,持续沉淀和打磨形成适应不同养老业务发展需要的业务能力,推动业务模式升级,为养老服务提供数字化解决方案,并依托实体站点与养老机构实现线上线下…...
K8s 下通过prometheus监控 nginx
k8s 下有两个版本的nginx ingress 分别是 ingress nginx 以及 nginx ingress Ingress-nginx 与 Nginx-ingress - LeoZhanggg - 博客园 这里我讨论的是 nginx ingress Nginx Ingress 使用Prometheus 导出数据 nginx ingress 本身支持通过支持这个提供prometheus 格式的…...
基于SpringBoot的京东绿谷旅游信息服务平台设计与实现(源码+定制+开发)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
使用Redis生成全局唯一id
为了生成一个符合要求的分布式全局ID,我们可以使用 StringRedisTemplate 来实现。这个ID由三部分组成: 符号位(1 bit):始终为0,表示正数。时间戳(31 bit):表示从某个起始…...
pytorch自定义算子导出onnx
文章目录 1、为什么要自定义算子?2、如何自定义算子3、自定义算子导出onnx4、example1、重写一个pytorch 自定义算子(实现自定义激活函数)2、现有算子上封装pytorch 自定义算子(实现动态放大超分辨率模型) 1、为什么要…...
从搭建uni-app+vue3工程开始
技术栈 uni-app、vue3、typescript、vite、sass、uview-plus、pinia、axios 一、项目搭建 1、创建以 typescript 开发的工程 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project2、安装sass npm install -D sass// 安装sass-loader,注意需要版本10&…...
波点音乐自动点击
波点音乐 import uiautomator2 as u2 import time import sys import os# 动态点击时间,打印剩余时间 def dynamic_sleep(seconds):wait_time secondsfor i in range(wait_time):print(f"Waiting... {wait_time - i} seconds remaining")sys.stdout.flu…...
【一篇搞定配置】wget 下载与安装(Window)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀各种软件安装与配置_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1.…...
2025年春招修订版《C/C++笔面试系列》(1) C语言经典笔面试题(上)
C语言是嵌入式开发的常用语言,也是主要语言,很多企业在招聘嵌入式软件工程师时,C语言的熟练程度是一个重要考察点。笔试也是以C语言为主,所以想要拿到面试机会,还得通过C语言笔试,本专题总结了各大企业C语言…...
【042C】基于51RFID门禁系统(LCD12864显示)【Proteus仿真+Keil程序+报告+原理图】
☆、设计硬件组成:51单片机最小系统RFID读卡器4*4矩阵键盘AT24C02存储芯片LCD12864液晶显示继电器蜂鸣器LED灯。 1、设计采用STC89C52、AT89C52、AT89S52作为主控芯片,采用LCD12864液晶显示屏实时显示门禁系统; 2、系统集成两种解锁方式&am…...
scratch二次开发:blockly工作区垃圾桶和进度条的隐藏和显示
大家好,我是小黄。 本期给大家介绍的内容是实现blockly工作区的垃圾桶和进度条的显示和隐藏实现。 本次基于的项目源码大家可以关注小黄回复垃圾桶自行获取。 一.垃圾桶的显示和实现。 在blockly中,我们进行块的删除的时候最常用的两种方法是…...
【STM32】MPU6050初始化常用寄存器说明及示例代码
一、MPU6050常用配置寄存器 1、电源管理寄存器1( PWR_MGMT_1 ) 此寄存器允许用户配置电源模式和时钟源。 DEVICE_RESET :用于控制复位的比特位。设置为1时复位 MPU6050,内部寄存器恢复为默认值,复位结束…...
LDR6020驱动的Type-C接口显示器解决方案
一、引言 随着科技的飞速发展,Type-C接口凭借其高速数据传输、强大的电力传输能力以及便捷的正反可插设计,正逐渐成为现代电子设备的主流接口标准。在显示器领域,Type-C接口的引入不仅简化了线缆连接,还为用户带来了更丰富的功能…...
Spring Boot + Android 实现登录功能
在移动互联网的今天,许多应用需要通过移动端实现与服务器的交互功能,其中登录是最常见且基础的一种功能。通过登录,用户可以获得独特的身份标识,从而访问特定的资源或服务。本篇博客将详细介绍如何使用 Spring Boot 和 Android 实…...
【通俗理解】边际化技巧在概率论中的应用——从公式到实例
【通俗理解】边际化技巧在概率论中的应用——从公式到实例 关键词提炼 #边际化技巧 #概率论 #联合概率 #条件概率 #积分计算 #概率分布 #贝叶斯推断 第一节:边际化技巧的类比与核心概念【尽可能通俗】 边际化技巧,就像是你在一个复杂的概率迷宫中&am…...
Chen_AdaMV-MoE_Adaptive_Multi-Task_Vision_Mixture-of-Experts 译文
摘要 稀疏激活的专家混合(MoE)正在成为多任务学习(MTL)的一个有前途的范例。 MoE 不是将多个任务的知识压缩到单个模型中,而是分离参数空间,仅利用给定任务类型及其输入的相关模型片段,从而提供…...
多线程下使用数据库 - 20241124
问题 并发性较低,每秒千次但是较高一致性,比如利用数据库中的数据进行判断是否执行某个操作 存在的问题是,如何只锁定判断相关的数据,而不影响数据库操作无关数据。脏读/不可重复读/幻读 解决方案 利用数据InnoDB引擎的LBCC和…...
GMAN解读(论文+代码)
一、注意力机制 注意力机制与传统的卷积神经网络不同的是,前者擅长捕获全局依赖和长程关系,权重会动态调整。而后者对于所有特征都使用同一个卷积核。关于更多注意力机制内容,详见: 注意力机制、自注意力机制、多头注意力机制、通…...
《文件操作》
一 . 文本文件和二进制文件 根据数据的组织形式,数据文件被分为了二进制文件和文本文件 数据在内存中是以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。 如果要求在外存上以ASCII 码的形式存储,则需要再存…...
【君正T31开发记录】8.了解rtsp协议及设计模式
前边搞定了驱动,先不着急直接上手撸应用层的代码,先了解一下大致要用到的东西。 设计PC端先用vlc rtsp暂时H264编码(vlc好像不支持h265,这个后边我试试)的视频流,先需要支持上rtsp server,了解rtsp协议是必…...
C++菜鸟教程 - 从入门到精通 第三节
上节课的题 上节课的题,大家都做出来了吗? 看一下这道题: 题目描述 N!1*2*...*N; 例5!1*2*3*4*5120. 编程求1!2!3!...N!. 输入 输入一行,只有一个整数n (1<n<10) 输出 输出只有一行(这意味着末尾有一个回车符号),包括1…...
原生JS和CSS,HTML实现开屏弹窗
开屏弹窗常应用于打开游戏,或者打开网站的时候,跳出来在正中间,来显示一些信息,并可以设置今日不再显示 CSS代码如下 <style>#box {width: 100vw;height: 100vh;background-color: rgba(0, 0, 0, 0.2);position: relative…...
微软发布Win11 24H2系统11月可选更新KB5046740!
系统之家11月22日报道,微软针对Win11 24H2系统推出2024年11月最新可选更新补丁KB5046740,更新后系统版本后升至26100.2454,此次更新后修复当应用程序以PDF和XLSX格式导出图表对象时停止响应、无法使用API查找旋转信息等问题。以下小编将给大家…...
【Rabbitmq篇】高级特性----TTL,死信队列,延迟队列
目录 一.TTL 1.设置消息的TTL 2.设置队列的TTL 3.俩者区别 二.死信队列 定义: 消息成为死信的原因: 1.消息被拒绝(basic.reject 或 basic.nack) 2.消息过期(TTL) 3.队列达到最大长度 编辑 …...
【Linux】gcc/g++使用
编译 我们知道,gcc只能编译C,g既能编译C,也能编译C。 由于两者的选项是相同的,这里我们使用gcc来说明。 这就是一个我们在linux中gcc编译一段代码后会自动生成一个a.out为名的可执行文件,然后我们./a.out,…...
IDEA2023 SpringBoot整合MyBatis(三)
一、数据库表 CREATE TABLE students (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100) NOT NULL,age INT,gender ENUM(Male, Female, Other),email VARCHAR(100) UNIQUE,phone_number VARCHAR(20),address VARCHAR(255),date_of_birth DATE,enrollment_date DATE,cours…...
Java网络编程 - cookiesession
cookie 之前学习了 Okhttp3 库可以调用API、抓取网页、下载文件。但是这些操作都是不要求登录的,如果 API、网页、文件等内容要求登录才能访问,就需要学习新的 cookie 相关的知识。 下面以豆瓣为例,使用 Java 程序读取“我的豆瓣”页面内容…...
100.【C语言】数据结构之二叉树的堆实现(顺序结构) 1
目录 1.顺序结构 2.示意图 编辑 从物理结构还原为逻辑结构的方法 3.父子节点编号的规律 4.顺序存储的前提条件 5.堆的简介 堆的定义 堆的两个重要性质 小根堆和大根堆 6.堆的插入 7.堆的实现及操作堆的函数 堆的结构体定义 堆初始化函数HeapInit 堆插入元素函…...
《Python基础》之循环结构
目录 简介 一、for循环 1、基本语法与作用 2、使用 range() 函数配合 for 循环 3、嵌套的for循环 二、while循环 1、基本语法与作用 2、while 循环嵌套 (1)、while循环与while循环嵌套 (2)、while循环与for循环嵌套 简介 …...
使用JDBC操作数据库
文章目录 使用JDBC操作数据库1. JDBC访问数据库步骤2. Statement与PreparedStatement区别3. JDBC的内容4. JDBC封装4.1 为什么进行JDBC封装4.2 实现JDBC封装4.3 什么是DAO4.4 配置数据库访问参数4.5 配置数据库连接池使用之JNDI的方式 5. 单例模式5.1 懒汉模式5.2 饿汉模式 使用…...
轻松解析 PDF 文档:深入了解 Python 的 pdfplumber 库
轻松解析 PDF 文档:深入了解 Python 的 pdfplumber 库 PDF 是一种常见的文件格式,广泛用于报告、文档、表单等领域。然而,如何高效解析 PDF 内容(尤其是文本和表格),一直是开发者面临的挑战。pdfplumber 是…...
实验五 时域采样与频域采样
时域采样理论的验证 【实例3-1】近似绘制x (n) R4n 在(0,2 π \pi π ) 上的幅频响应曲线( F T [ x ( n ) ] FT[x(n)] FT[x(n)] )。 x [1, 1, 1, 1]; N 64; xk fft(x, N); figure; subplot(2, 1, 1); stem(0:3, x, .); subplot(2, 1, 2); k 0:N-1; plot(2*k/N, abs(x…...
爬虫cookie反爬------加速乐(jsl)
加速乐 反爬虫技术:加速乐采用了包括OB混淆、动态加密算法和多层Cookie获取等高级反爬虫技术,确保整体校验的严密性。关键校验字段位于Cookie中的 __jsl_clearance_s,其验证过程通常涉及三次关键的请求,有效抵御恶意爬虫的侵扰。…...
设计模式——解释器模式
定义: 解释器模式是一种行为设计模式,它给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在这种模式中,通常会将一个复杂的表达式(如数学表达…...
sorted()函数
sorted(iterable, keyNone, reverseFalse)iterable: 需要排序的可迭代对象(如列表、元组、字符串等)。 key: 一个函数,用于从每个元素中提取排序的依据。如果未指定,默认直接比较元素本身。 reverse: 一个布尔值,Tru…...
动静态分析
静态分析 获取哈希值: 查壳: 导出函数: 获取资源信息: 通过发现dos头和pe头,来确定它是个可执行程序。 动态分析...
2024年信号处理与神经网络应用国际学术会议(SPNNA 2024)
重要信息 会议时间:2024年12月13-15日 会议地点:中国武汉 会议官网:www.spnna.org 会议简介 2024年信号处理与神经网络应用国际学术会议(SPNNA 2024)将于2024年12月13日至15日在中国武汉召开。本次会议旨在为全球研…...
winfrom快速自适应
在软件界面设计中,我们通常需要添加各种布局器和规则来实现界面布局,但对于不太熟练的工程师来说,这可能存在一定难度。这里要分享一种自适应布局的方法,它可以根据界面比例自动缩放内容控件,在较短时间内完成软件布局…...
VMware16安装macOS12【详细教程】
因为在应用上线IOS应用商店时,需要用到mac系统进行,于是就在VMware16pro虚拟机进行安装macOS12系统,安装的过程做了一个记录,希望对你有所帮助! 前言 首先需要下载好下面工具: VMware workstation pro 16…...
【设计模式】【创建型模式(Creational Patterns)】之单例模式
单例模式是一种常用的创建型设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。 单例模式的原理 单例模式的核心在于控制类的实例化过程,通常通过以下方式实现: 私有化构造函数,防止外部直接实例化。…...