【SkiaSharp绘图08】SKPaint方法:自动换行、是否乱码、字符偏移、边界、截距、文本轮廓、测量文本
文章目录
- SKPaint方法
- BreakText 计算指定宽度内可绘制的字符个数
- ContainsGlyphs字体是否包含文本字符(是否会乱码)
- GetGlyphOffsets 字符偏移量
- GetGlyphPositions 偏移坐标
- GetGlyphWidths 每个字符的宽度与边界
- GetHorizontalTextIntercepts 轮廓截距
- GetPositionedTextIntercepts轮廓截距
- GetTextIntercepts 轮廓截距
- 代码示例
- GetTextPath 文本轮廓路径
- MeasureText 测量文本
- Reset重置
- Clone 克隆
- ToFont
- SetColor
- 其它待补充
- MaskFilter 掩码过滤器
- PathEffect 路径效果
- Shader 着色器
SKPaint方法
BreakText 计算指定宽度内可绘制的字符个数
public long BreakText (string text, float maxWidth);
public long BreakText (byte[] text, float maxWidth, out float measuredWidth);
指定要测量的文本和最大文本绘制宽度,获取指定宽度内可绘制的字符个数和实际绘制宽度。
实现一个可自动换行绘制的函数
/// <summary>
/// 绘制自动换行的文本
/// </summary>
/// <param name="canvas"></param>
/// <param name="text">待绘制的文本</param>
/// <param name="maxWidth">文本最大宽度</param>
/// <param name="left">文本框左上角X轴起始</param>
/// <param name="top">文本框左上角Y轴起始</param>
/// <param name="paint"></param>
/// <param name="endWidth">最后一行实际绘制宽度</param>
/// <returns>总文本框高度</returns>
public float DrawTextWithWordWrap(SKCanvas canvas, string text, float maxWidth, float left, float top, SKPaint paint,out float endWidth)
{endWidth = 0F;if (string.IsNullOrEmpty(text)) return 0.0f;//文本基线float baselineY = top - paint.FontMetrics.Ascent;while (true){var breakIndex = (int)paint.BreakText(text, maxWidth, out endWidth);string line = text.Substring(0, breakIndex);canvas.DrawText(line, left, baselineY, paint);text = text.Substring(breakIndex).TrimStart(); // 去掉已经绘制的部分,继续处理剩余的文本if (!string.IsNullOrEmpty(text)){baselineY += paint.FontSpacing;}else{break;}}return baselineY + paint.FontMetrics.Descent - top;
}
给定文本框左上角起始点、文本框宽度、文本内容。绘制后,将文本框绘制一个矩形,并在结尾位置画一蓝色竖线。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);const string TEXT = "SkiaSharp 是一个跨平台的 2D 图形库,用于 .NET 平台。它是 Skia 图形引擎的 .NET 绑定,提供了高性能的绘图和图形处理功能。SkiaSharp 允许开发人员在 .NET 平台上创建丰富的用户界面、图像处理应用程序、游戏和数据可视化工具。";using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.TextSize = 24;paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");var edge = 20;var top = 50;var txtWidth = Math.Max(30, info.Width - edge * 2);var height = DrawTextWithWordWrap(canvas, TEXT, txtWidth, edge, top, paint,out float endWidth);paint.Color = SKColors.Red;paint.IsStroke = true;//实际的文本框canvas.DrawRect(edge, top, txtWidth, height, paint);//文本结尾的位置paint.Color = SKColors.Blue;paint.StrokeWidth = 5;canvas.DrawLine(edge + endWidth, top + height - paint.FontSpacing, edge + endWidth, top + height,paint);
}
ContainsGlyphs字体是否包含文本字符(是否会乱码)
public bool ContainsGlyphs (string text);
public bool ContainsGlyphs (byte[] text);
获取指定文本所有字符是否对应于非零字形索引(不会乱码)。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);const string TEXT = "SkiaSharp绘图";using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.TextSize = 24;paint.Typeface = SKTypeface.FromFamilyName("Arial");//无法显示中文canvas.DrawText($"{TEXT},{paint.Typeface.FamilyName},ContainsGlyphs:{paint.ContainsGlyphs(TEXT)}",20,50,paint);paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");//可以显示中文canvas.DrawText($"{TEXT},{paint.Typeface.FamilyName},ContainsGlyphs:{paint.ContainsGlyphs(TEXT)}", 20, 100, paint);
}
判断指定的字体是否会显示乱码。
GetGlyphOffsets 字符偏移量
public float[] GetGlyphOffsets (string text, float origin = 0);//origin相对起始偏移
用于获取给定文本中每个字形的偏移量。这对于在进行复杂的文本布局时非常有用,因为它允许您精确控制每个字形的位置,而不仅仅是使用默认的字符间距和行间距。
获取一句文本中每个字符的偏移量,并在字符前画一竖线。
GetGlyphPositions 偏移坐标
public SkiaSharp.SKPoint[] GetGlyphPositions (string text, SkiaSharp.SKPoint origin = default);
获取每个字符的偏移坐标
GetGlyphWidths 每个字符的宽度与边界
public float[] GetGlyphWidths (string text, out SkiaSharp.SKRect[] bounds);
获取每个字符占的宽度和边界。
GetHorizontalTextIntercepts 轮廓截距
public float[] GetHorizontalTextIntercepts (string text, float[] xpositions, float y, float upperBounds, float lowerBounds);
获取文本与两条平行相交的截距。(只能单字?多字时,其它坐标不知如何处理)
xpositions:为文本起始位置
y:为文本垂直的基线位置
upperBounds:上水平线
lowerBound:下水平线,这两条线表示需要测量的范围。
GetPositionedTextIntercepts轮廓截距
public float[] GetPositionedTextIntercepts (string text, SkiaSharp.SKPoint[] positions, float upperBounds, float lowerBounds);
与GetHorizontalTextIntercepts 基本相同。
GetTextIntercepts 轮廓截距
public float[] GetTextIntercepts (string text, float x, float y, float upperBounds, float lowerBounds);
获取文字与平行线的截距,有前面函数的效果基本相同。
代码示例
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);const string TEXT = "SkiaSharp绘图";using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.TextSize = 48;paint.TextScaleX = 1F;var startX = 20F;var startY = 100F;paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");canvas.DrawText($"{TEXT}", startX, startY, paint);//x偏移量var xOffsets = paint.GetGlyphOffsets(TEXT, startX);paint.Color = SKColors.Red;foreach(var x in xOffsets){canvas.DrawLine(x, 80, x, 100, paint);}//x偏移量var points = paint.GetGlyphPositions(TEXT, new SKPoint(startX, startY));paint.Color = SKColors.Blue;foreach (var pt in points){canvas.DrawLine(pt.X, pt.Y, pt.X, pt.Y + 20, paint);}startY += 100;canvas.DrawText($"{TEXT}", startX, startY, paint);//宽度与边界var widths = paint.GetGlyphWidths(TEXT, out SKRect[] bounds);paint.Color = SKColors.Red;float offsetX = startX;paint.IsStroke= true;var index = 0;foreach(var rect in bounds){//绘制边界矩形canvas.DrawRect(offsetX + rect.Left, rect.Top + startY, rect.Width, rect.Height, paint);offsetX += widths[index++];}// 指定文本的起始 X 坐标数组float[] xpositions = { 100 };// 文本的起始 Y 坐标float y = 350;// 指定测量区域的上边界和下边界 Y 坐标float upperBounds = 280;float lowerBounds = 340;paint.TextSize = 120;// 获取文本的水平截距float[] intercepts = paint.GetHorizontalTextIntercepts("心", xpositions, y, upperBounds, lowerBounds);// 绘制文本canvas.DrawText("心", xpositions[0], y, paint);paint.TextSize = 24;canvas.DrawText("GetHorizontalTextIntercepts", xpositions[0] + 250, y-30, paint);paint.TextSize = 120;//两条水平线paint.Color = SKColors.Green;canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);// 绘制截距线paint.Color = SKColors.Red;paint.StrokeWidth = 2;foreach (var intercept in intercepts){canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);}y += 100;upperBounds += 100;lowerBounds += 100;var skpoints = new SKPoint[] { new SKPoint(xpositions[0], y) };intercepts = paint.GetPositionedTextIntercepts("心", skpoints, upperBounds, lowerBounds);// 绘制文本canvas.DrawText("心", xpositions[0], y, paint);paint.TextSize = 24;canvas.DrawText("GetPositionedTextIntercepts", xpositions[0] + 250, y - 30, paint);paint.TextSize = 120;//两条水平线paint.Color = SKColors.Green;canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);// 绘制截距线paint.Color = SKColors.Red;paint.StrokeWidth = 2;foreach (var intercept in intercepts){canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);}y += 100;upperBounds += 100;lowerBounds += 100;intercepts = paint.GetTextIntercepts("心", xpositions[0],y, upperBounds, lowerBounds);// 绘制文本canvas.DrawText("心", xpositions[0], y, paint);paint.TextSize = 24;canvas.DrawText("GetTextIntercepts", xpositions[0] + 250, y - 30, paint);paint.TextSize = 120;//两条水平线paint.Color = SKColors.Green;canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);// 绘制截距线paint.Color = SKColors.Red;paint.StrokeWidth = 2;foreach (var intercept in intercepts){canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);}
}
运行结果示例
GetTextPath 文本轮廓路径
public SkiaSharp.SKPath GetTextPath (string text, float x, float y);
public SkiaSharp.SKPath GetTextPath (string text, SkiaSharp.SKPoint[] points);
用于生成给定文本的路径数据。路径数据包含文本的轮廓信息,可以用于高级文本渲染和图形操作,例如自定义文本效果、路径动画、变形、剪切等。通过这个方法,开发者可以获取文本的几何形状,并将其用于各种自定义绘制操作。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);const string TEXT = "SkiaSharp绘图";using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.TextSize = 96;paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");var x = 20F;var y = 100F;canvas.DrawText(TEXT, x,y, paint);//文本轮廓路径var textPath = paint.GetTextPath(TEXT,x,y);paint.StrokeWidth = 2;paint.IsStroke = true;paint.Color = SKColors.Red;canvas.DrawPath(textPath, paint);
}
使用DrawText绘制填充黑色文本,再使用GetTextPath获取文本轮廓,再使用DrawPath绘制文本红色描边轮廓。
MeasureText 测量文本
public float MeasureText (string text);
public float MeasureText (string text, ref SkiaSharp.SKRect bounds);
用于测量给定文本字符串的宽度。这个方法的主要作用在于计算文本在指定字体和样式下的绘制尺寸,通常用于布局和排版,确保文本能够正确地放置在指定的绘制区域内。
var canvas = e.Surface.Canvas;var info = e.Info;canvas.Clear(SKColors.White);const string TEXT = "SkiaSharp绘图";using (var paint = new SKPaint()){paint.IsAntialias = true;paint.TextSize = 96;paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");var x = 20F;var y = 100F;var rect=new SKRect();var width = paint.MeasureText(TEXT, ref rect);canvas.DrawText(TEXT,x,y, paint);paint.IsStroke = true;canvas.DrawRect(rect.Left + x, y + rect.Top, rect.Width, rect.Height, paint);//修改字体、水平放大1.1倍y +=200;paint.Typeface = SKTypeface.FromFamilyName("宋体");paint.TextScaleX = 1.1F;paint.Color = SKColors.Red;width = paint.MeasureText(TEXT, ref rect);canvas.DrawText(TEXT, x, y, paint);canvas.DrawRect(rect.Left + x, y + rect.Top, rect.Width, rect.Height, paint);}
测量相同文本、两种字体的宽度和边界矩形,并在文字四边绘制边界矩形。
使用场景
-
自动换行:
- 根据测量的文本宽度决定何时换行,以适应固定宽度的文本容器。
-
对齐和布局:
- 根据测量结果调整文本的位置,实现文本的居中对齐、左对齐或右对齐。
-
文本容器调整:
- 根据文本宽度动态调整文本容器的大小或位置,确保文本能够正确显示。
Reset重置
public void Reset ();
重置 SKPaint 对象的所有属性到它们的默认状态。这在绘图过程中特别有用,可以确保在开始绘制新的图形或文本之前,所有的绘图设置都是清空的,从而避免意外影响之前的绘图设置。
Clone 克隆
public SkiaSharp.SKPaint Clone ();
浅拷贝SKPaint对象。
ToFont
public SkiaSharp.SKFont ToFont ();
获取SKPaint对应的SKFont
SetColor
public void SetColor (SkiaSharp.SKColorF color, SkiaSharp.SKColorSpace colorspace);
用于设置 SKPaint 对象的颜色属性,并指定颜色空间。这个方法允许你使用浮点颜色和指定的颜色空间来设置绘制操作的颜色属性。
浮点数范围(从0到1)表示RGBA分量。
其它待补充
MaskFilter 掩码过滤器
用于在绘制时应用掩码过滤效果。掩码过滤器可以对绘制的内容进行模糊处理、锐化处理、浮雕效果等特殊效果的处理。通过使用SKPaint.MaskFilter,你可以改变绘制对象的外观,增加视觉效果。
PathEffect 路径效果
用于对路径进行特殊效果处理。路径效果可以对绘制的路径进行各种转换和修改,比如虚线效果、路径的偏移效果、路径变换等。通过使用SKPaint.PathEffect,你可以增加绘制路径的复杂性和美观度。
Shader 着色器
用于创建和应用复杂的填充效果。着色器(Shader)可以用于填充形状和路径,产生渐变、图案填充、图像纹理等效果。通过使用SKPaint.Shader,你可以为绘制的内容添加丰富的视觉效果。
相关文章:

【SkiaSharp绘图08】SKPaint方法:自动换行、是否乱码、字符偏移、边界、截距、文本轮廓、测量文本
文章目录 SKPaint方法BreakText 计算指定宽度内可绘制的字符个数ContainsGlyphs字体是否包含文本字符(是否会乱码)GetGlyphOffsets 字符偏移量GetGlyphPositions 偏移坐标GetGlyphWidths 每个字符的宽度与边界GetHorizontalTextIntercepts 轮廓截距GetPositionedTextIntercepts…...
深入理解Servlet Filter及其限流实践
引言 在Java Servlet技术中,Filter是一个拦截器,它允许开发者在请求到达目标资源之前或响应发送给客户端之后,对请求或响应进行拦截和处理。这种机制为实现诸如身份验证、日志记录、请求修改等功能提供了极大的灵活性。 Filter基础 Filter…...
使用cv2对视频指定区域进行去噪
视频去噪其实和图象一样,只是需要现将视频截成图片,在对图片进行去噪,将去噪的图片在合成视频就行。可以利用cv2.imread()、imwrite()等轻松实现。 去噪步骤 1、视频逐帧读成图片 2、图片指定区域批量去噪 2、去噪后的图片写入视频 1、视频逐…...
AI在创造还是毁掉音乐?
AI对音乐产业的影响是复杂而多维的,既有创造性的贡献也存在潜在的挑战。我们可以从以下几个角度来分析这个问题: ### 创造性贡献 1. **音乐创作**:AI可以帮助音乐家创作新的旋律和和声,甚至生成完整的音乐作品。例如,…...
【2023年全国青少年信息素养大赛智能算法挑战赛复赛真题卷】
目录 2023全国青少年信息素养大赛智能算法挑战赛初中组复赛真题 2023全国⻘少年信息素养⼤赛智能算法挑战复赛⼩学组真题 2023全国青少年信息素养大赛智能算法挑战赛初中组复赛真题 1. 修复机器人的对话词库错误 【题目描述】 基于人工智能技术的智能陪伴机器人的语言词库被…...

Android系统揭秘(一)-Activity启动流程(上)
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread (IApplicationThread) contextThread; … try { … int result …...
使用Java实现哈夫曼编码
前言 哈夫曼编码是一种经典的无损数据压缩算法,它通过赋予出现频率较高的字符较短的编码,出现频率较低的字符较长的编码,从而实现压缩效果。这篇博客将详细讲解如何使用Java实现哈夫曼编码,包括哈夫曼编码的原理、具体实现步骤以…...

IDEA、PyCharm等基于IntelliJ平台的IDE汉化方式
PyCharm 或者 IDEA 等编辑器是比较常用的,默认是英文界面,有些同学用着不方便,想要汉化版本的,但官方没有这个设置项,不过可以通过插件的方式进行设置。 方式1:插件安装 1、打开设置 File->Settings&a…...

visual studio 创建c++项目
目录 环境准备:安装 visual studiovisual studio 创建c项目Tips:新建cpp文件注释与取消注释代码 其他初学者使用Visual Studio开发C和C时常遇到的3个坑 环境准备:安装 visual studio 官网:https://visualstudio.microsoft.com/zh…...

MGV电源维修KUKA机器人电源模块PH2003-4840
MGV电源维修 库卡电源模块维修 机器人电源模块维修 库卡控制器维修 KUKA电源维修 库卡机器人KUKA主机维修 KUKA驱动器模块维修 机械行业维修:西门子系统、法那克系统、沙迪克、FIDIA、天田、阿玛达、友嘉、大宇系统;数控冲床、剪板机、折弯机等品牌数控…...

设置浏览器互不干扰
目录 一、查看浏览器文件路径 二、 其他盘新建文件夹Cache 三、以管理员运行CMD 四、执行命令 一、查看浏览器文件路径 chrome://version/ 二、 其他盘新建文件夹Cache D:\chrome\Cache 三、以管理员运行CMD 四、执行命令 Mklink /d "C:\Users\Lenovo\AppData\Loca…...
kafka操作命令详解
目录 1、集群运维命令 1.1、集群启停命令 1.3、集群迁移命令 1.4、权限管理命令 1.4.1、权限参数介绍 1.4.2、增加权限命令 1.4.3、移出权限命令 1.4.4、查看所有topic权限命令 1.4.5、查看某个topic权限命令 2、生产者命令 2.1、创建topic命令 2.2、删除topic命令 …...

graalvm jdk和openjdk
下载地址:https://github.com/graalvm/graalvm-ce-builds/releases 官网: https://www.graalvm.org...

docker基础使用教程
1.准备工作 例子:工程在docker_test 生成requirements.txt文件命令:(使用参考链接2) pip list --formatfreeze > requirements.txt 参考链接1: 安装pipreqs可能比较困难 python 项目自动生成环境配置文件require…...

计算机网络 交换机的安全配置
一、理论知识 1.交换机端口安全功能介绍 交换机端口安全功能是针对交换机端口进行安全属性的配置,以控制用户的安全接入。主要包括以下两种配置项: ①限制交换机端口的最大连接数:控制交换机端口连接的主机数量;防止用户进行恶…...
深入解析大语言模型系列:Transformer架构的原理与应用
引言 在自然语言处理(NLP)领域,大语言模型(Large Language Models, LLMs)近几年取得了突破性的进展,而 Transformer 作为这些模型的核心架构,功不可没。本文将详细介绍 Transformer 的原理、结…...
uni-app地图组件控制
uni.createMapContext(mapId,this) 创建并返回 map 上下文 mapContext 对象。在自定义组件下,第二个参数传入组件实例this,以操作组件内 <map> 组件。 注意:uni.createMapContext(mapId, this) app-nvue 平台 2.2.5 支持 uni.create…...
前端调用api发请求常用的请求头content- type的类型和常用场景
Content-Type 是一个非常重要的HTTP头,它定义了发送给服务器或客户端的数据的MIME类型。这对于服务器和客户端正确解析和处理数据至关重要。下面是一些常见的 Content-Type 值及其用途和区别。 常见的 Content-Type 值 text/plain • 用途: 纯文本,无格…...
数据仓库之SparkSQL
Apache Spark SQL是Spark中的一个组件,专门用于结构化数据处理。它提供了通过SQL和DataFrame API来执行结构化数据查询的功能。以下是对Spark SQL的详细介绍: 核心概念 DataFrame: 定义: DataFrame是一个分布式数据集合,类似于关系型数据库中…...
如何在 MySQL 中导入和导出数据库以及重置 root 密码
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 如何导入和导出数据库 导出 要导出数据库,打开终端,确保你没有登录到 MySQL 中,然后输入以下命令&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...