百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

为何大厂的图片不会OOM

toyiye 2024-05-19 19:35 13 浏览 0 评论

码个蛋(codeegg) 第 980 次推文

作者:Like_Codeing链接:https://juejin.im/post/5ec7302c518825434062f497

图片在移动开发中占据中举足轻重的地位,早期的android 应用页面Ui相对简单,但随着Android系统不断的升级发展, 界面元素越来越丰富,用户对体验要求越来越高,UI小姐姐们需要设计出精致的界面元素,其中不乏很多好看的图片,但是随着手机性能提升(分辨率,cpu主频,内存等),图片质量也越来越大,拍个照动不动就3M,4M,8M, 大家都知道,android 应用在创建进程时候,会分配一个指定的内存大小,准确的说话是 google原生OS的默认值是16M,但是各个厂家的系统会对这个值进行修改,如果我们应用“毫不吝啬”将这些大图直接加载到内存中,很快内存就会耗尽,最终出现OOM异常,所以图片的处理对于一个稳定、用户体验友好的应用来说非常重要,今天我们就来聊一聊Bitmap,在开发过程中把”图片“给优化一番,保证我们项目在线上稳定、流畅运行。

初识

Bitmap图像处理的最重要类之一,用它可以获取图像文件信息,进行图像颜色变换、剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

如图,bitmap在sdk中算是元老级的人物了,从api1中就已经有了,可见其重要性。

继承关系就不解释了,实现了Parcelable 具备在内存中传递的特性。

bitmap中有两个重要的内部类 CompressFormat 以及 Config;

下面分别介绍一下这两个类

  • CompressFormat

CompressFormat 是用来设置压缩方式的,是个枚举类,内部提供了三种图片压缩方式类型,

  1. JPEG :表示Bitmap采用JPEG压缩算法进行压缩,压缩后的格式可以是.jpg或者.png,是一种有损压缩方式。

  2. PNG : 表示Bitmap采用PNG压缩算法进行压缩,压缩后的格式可以是.png,是一种无损压缩方式。

  3. WEBP :表示以WebP压缩算法进行图像压缩,压缩后的格式可以是".webp",是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%,美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”, 而且还需要注意,在官方文档中有这样的描述:As of Build.VERSION_CODES.Q, a value of100results in a file in the lossless WEBP format. Otherwise the file will be in the lossy WEBP format. 意为Android10之后如果quality值(压缩质量)为100的话,bitmap压缩采用无损压缩格式,其他都为有损压缩;

这里有的同志会问,这都是压缩格式啊,具体怎么操作压缩呢,Bitmap为我们提供了一个可靠的方法供开发者使用,我们来顺便看看Bitmap都有什么方法,如下:

第一个方法就是compress方法, 没错就是这么就这方法,一共有三个参数

  1. format :??上面已经说明了,表示压缩格式;

  2. quality :压缩质量,取值0-100,0表示最低画质压缩,100表示最高画质压缩,对于PNG压缩格式来说,该参数可以忽略,对于WEBP格式来说,小于100为有损压缩格式,会对画质产生直接影响, 等于100时候采用的是无损压缩格式,画质是不会有改变,但是图片大小得到很好压缩;

  3. stream :将压缩后的图片写到指定的输出流中;

返回值:boolean, 返回true表示成功将bitmap压缩到输出流中,然后可以通过Bitmap.Factory从相应的输入流中解析出来bitmap信息;

从官网介绍可知, 该方法在图片压缩过程中可能消耗较长时间,建议放在子线程中操作,至于为什么大家可以看看源码, 源码中会调用一个nativeCompress 的Native 方法,也就是压缩处理是放在底层处理的;

  • Config

表示位图像素的存储格式,什么意思呢? 就是bitmap在屏幕上显示的每一像素在内存中存储的格式,会影响Bitmap真实图片的透明度以及图片质量;

  1. Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位;

  2. Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位;

  3. Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位,是Bitmap 默认的颜色存储格式,也是最占空间的一种配置;

  4. Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位;

上面说了 android 系统默认存储位图方式是 ARGB_8888, 4个通道组成,每个通道8位,分表代表透明度和RGB颜色值, 也就是说一个位图像素占用了4个字节(1个byte8个bit位),

同理:采用 Bitmap.Config.RGB_565 存储,单像素占用内存大小仅有2byte,换句话说一张图片采用ARGB_565格式相对于默认的ARGB_8888内存将减少一半,所以通过改变bitmap像素存储方式也是图片内存优化的重要渠道,这个后面会讲到;

BitmapFactory

创建位图bitmap对象途径有很多种, 包括指定文件、流, 和字节数组等;

官方文档中提供了从字节数组、指定路径,系统Resource、二进制流等方式创建Bitmap, 当然有的方法需要一些特殊参数,例如通过字节数组方式需要指定解析的起始偏移位置,长度等,有的需要指定路径 path , 或者指定 BitmapFactory.Option配置信息 , 它也是我们图片优化的重要手段;

BitmapFactort.Options这个是什么鬼呢, 很重要!bitmap加载的配置类,想要做图片内存优化是少不了跟它打“打交道”,如下其内部属性

这里我们大概只说跟图片优化相关的几个重要属性

  • insampleSize :采样率,默认1表示无缩放,等于2表示宽高缩放2倍,总大小缩小4倍;

  • inBitmap :被复用的bitmap;

  • inJustDecodeBound : 如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息;

  • inMutable :是否图片内容可变,如果Bitmap复用的话,需要设置为true;

  • inDensity :加载bitmap时候对应的像素密度(后面会讲到);

  • inTargetDensity :bitmap位图被真实渲染出的像素密度,对应终端设备的屏幕像素密度(后面会讲到);

好了,Bitmap的api我们就讲到这里,因为我们今天不是主要讲解他的用法,为了给接下来的知识做一个铺垫,简单介绍bitmap的知识点,我们接下来回归”正题“

Bitmap 占用内存分析

Bitmap 用来描述一张图片的长、宽、颜色等信息。通常情况下,我们可以使用 BitmapFactory 来将某一路径下的图片解析为 Bitmap 对象。

当一张图片加载到内存后,具体需要占用多大内存呢?

  • getAllocationByteCount

  • getByteCount

  • getRowBytes

这三个方法是什么意思呢?跟内存占用又有什么关系呢,下面我们分别解释一下这三个方法

先一下这张图

上图中 是保存在 res/drawable-mdpi 目录下的一张 1920*1200,大小为 270Kb 的图片

为什么让你看这张图呢? 因为眼睛??看累了,顺便。。。 不是的,注意上面红色框中原始图片大小和尺寸,为后面压缩设定主题;

我们分别通过 Bitmap.getAllocationByteCount 以及 Bitmap.getByteCount和Bitmap.getRowBytes 方法获取 该Bitmap 的相关字节大小,比如以下代码:

打印结果如下

2020-05-23 10:20:10.926 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 10:21:52.422 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1800  width:2880  allocationByteCount:20736000  byteCount:20736000  rowBytes:11520  density:240  mutable:false

大家看到 allocationByteCount = byteCount = 20736000 为什么呢? 两者又有什么差距呢?

这里我们看看官方文档怎么说的:

而且在api19之后系统推荐使用getAllocationByteCount,看源码

所以上面日志信息两者相等是成立,那这两者又跟getRowBytes有什么关系呢?我们接着往下看,当我打开getByteCount的源码你就瞬间明白了

getByteCunt内存大小其实就是一行像素所占据字节大小 * Bitmap高度

我们可以验证一下:

11520 * 1800 = 20736000

结算结果非常准确,没有任何偏差,大小类似理解一个矩形面积等于长*宽一样 , getRowBytes代表就是该bitmap一行像素所占据的内存大小,然后再乘以高度就是整张bitmap所占用内存;

或许有的朋友又会问,那getRowBytes大小怎么来的呢?总得给个解释吧, 刚才上面解释了,它代表了bitmap一行的像素内存,这又什么意思呢?一行像素所占用内存=bitmap宽度 * 1像素所占字节大小 ,计算如下

2880 * 4 = 11520

计算结果同样没有任何偏差,此时大家是不是似乎明白了一些什么, 我这里是根据 bitmap 内存相关api 从内到外跟大家分析内存占用, 最终得出结论

Bitmap占用内存= 宽 * 高 * 一像素所占用字节内存 ,如下

2880 * 1800 * 4 = 20736000

可能有的同志发现了,内存中bitmap图片高度、宽度跟原始图片宽高不一样,这是为什么呢?

是的,确实不一样,这里有个细节知识点,我们上面在讲Bitmap相关api时候也提到过inDensity和InTargetDensity,我这里先说出结论,然后在带大家从源码角度上找答案;

实际上 BitmapFactory 在解析图片的过程中,会根据当前设备屏幕密度和图片所在的 drawable 目录来做一个对比,根据这个对比值进行缩放操作。具体公式为如下所示:

缩放比例 scale = 当前设备屏幕密度 / 图片所在 drawable 目录对应屏幕密度

Bitmap 实际占用内存 = 宽 * scale * 高 * scale * 一像素所占用字节内存,在 Android 中,各个 drawable 目录对应的屏幕密度分别为下:

在回头看我们上面那个问题,为什么图片原始宽高跟bitmap宽高不等,从我们打印的日志可知我们设备density=1.5 densityDpi=240,而图片放在drawable-mdpi , 该bitmap的desityDpi为160 ,

 bitmap 真实高= 1.5 (设备densityDpi 240/图片所在drawable的densityDpi 160 ) * 1200 = 1800
bitmap 真实宽= 1.5 (设备densityDpi 240/图片所在drawable的densityDpi 160 ) * 1920 = 2800

同样结果非常准确,也就是说明我们Bitmap内存大小除了跟我们图片宽高有关系、Bitmap.Config 以及 缩放比,而缩放比大小取决于 设备屏幕密度和图片所在drawable对应密度。

如果我们把图片放到drawable-hdpi下面,bitmap内存大小会有变化么? 是变大了还是变小了?

我是打印一下日志试一下, 然后再根据上面那个规则验证一下结果,打印如下

2020-05-23 12:01:45.358 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 12:01:47.018 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:9216000 byteCount:9216000 rowBytes:7680 density:240 mutable:false

Bitmap的宽高等于原始宽高,内存大小 9216000 ,原因就是图片的drawable的densityDpi变化了,根据公式大小计算

1920 * 240/240 * 1200 * 240/240 * 4 = 9216000

9216000/20736000 = 0.44..... 把图片放到mdpi下比在hdpi内存多消耗了60% 左右,

由此可见,我们在进行图片适配时候要准备多张图片放到不同drawable目录下,一方面保证了我们图片在各设备下的显示效果一致,另一方面系统加载适合的bitmap可以节省非常多内存空间,试想一下如果我们设备是640 Dpi的呢?而我们只准备了一张图片放在mdpi或者hdpi中,那么我们这张图片会消耗多大内存呀!!!

讲了这么多,Bitmap 占用内存大小我们已经总结出来了,那我们再看看源码验证一把,前面我们讲过BitmapFactory 解析Bitmap 相关api, 如:

public static Bitmap decodeResource(Resources res, int id, Options opts) {  validate(opts);  Bitmap bm = ;  InputStream is = ;  try {  final TypedValue value = new TypedValue;  is = res.openRawResource(id, value);  bm = decodeResourceStream(res, value, is, , opts);  } catch (Exception e) {  /* do nothing. If the exception happened on open, bm will be . If it happened on close, bm is still valid. */  } finally { try {  if (is != ) is.close;  } catch (IOException e) { // Ignore  } }  if (bm ==  && opts !=  && opts.inBitmap != ) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); }  return bm;}

继续跟进decodeResourceStream里面(这里就截图吧)

如果option为空就重新new一个出来,如果TypeValue不为空取出TypeValue的density信息,TypeValue是Resource解析对应资源时候的结果封装,这里就不详细解释了,大家可以自己学习一下, 从resource里面读取到图片信息后,包括该图片所在的drawable对一个的dpi,也就是TypeValue里面的density值,如果这个值为0的话此时就会用到系统的 认 DENSITY_DEFAULT,也就是这个值

public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;

public static final int DENSITY_MEDIUM = 160;

而inTargetDesity 大小为设备的屏幕密度 densityDpi

有了这两个值我们就可以计算bitmap的大小了, 我们接着看 decodeStream , 最终会跟到nativeDecodeStream 中, 很明显这是个native方法,因此我们知道Bitmap的内存计算其实是放在 native层做的, 那么我直接贴出native 层处理的代码吧,

static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { //初始缩放系数float scale = 1.0f;  if (env->GetBooleanField(options, gOptions_scaledFieldID)) {  const int density = env->GetIntField(options, gOptions_densityFieldID);  const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);  const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); if (density != 0 && targetDensity != 0 && density != screenDensity) { //缩放系数是当前系数密度/图片所在文件夹对应的密度; scale = (float) targetDensity / density;  }  }  //原始解码出来的Bitmap;SkBitmap decodingBitmap;  if(decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode) != SkImageDecoder::kSuccess) {  return ObjectReturn("decoder->decode returned false"); }  //原始解码出来的Bitmap的宽高; int scaledWidth = decodingBitmap.width;  int scaledHeight = decodingBitmap.height;  //要使用缩放系数进行缩放,缩放后的宽高; if(willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {  scaledWidth = int(scaledWidth * scale + 0.5f);  scaledHeight = int(scaledHeight * scale + 0.5f);  }  //源码解释为因为历史原因;sx、sy基本等于scale。 const float sx = scaledWidth / float(decodingBitmap.width);  const float sy = scaledHeight / float(decodingBitmap.height);  canvas.scale(sx, sy); canvas.drawARGB(0x00, 0x00, 0x00, 0x00);  canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);  // now create the java bitmap  return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset, bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);

native层计算如上处理,先获取图片原始宽高,根据decodeMode计算出缩放系数, 最后对canvas进行缩放,最后将Bitmap画出来从而完成Bitmap的加载操作, 如果看到这里大家基本已经了解Bitpmap加载到内存的流程和底层缩放策略了,不要停!继续聊,关于bitmap的优化还没开始讲。。。

  • assets 中的图片大小

我们知道,Android 中的图片不仅可以保存在 drawable 目录中,还可以保存在 assets 目录下,然后通过 AssetManager 获取图片的输入流。那这种方式加载生成的 Bitmap 是多大呢?同样是上面的 girl.png,这次将它放到 assets 目录中,使用如下代码加载:

最终打印结果如下:

2020-05-23 14:32:33.799 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:32:35.335 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:9216000 byteCount:9216000 rowBytes:7680 density:240 mutable:false

可以看出,加载 assets 目录中的图片,系统并不会对其进行缩放操作。

Bitmap 加载优化 上面的例子也能看出,一张 270Kb 大小的图片被加载到内存后,竟然占用了 9216000 个字节,也就是 9M 左右。因此适当时候,我们需要对需要加载的图片进行缩略优化。

修改图片加载的 Config

修改占用空间少的存储方式可以快速有效降低图片占用内存。比如通过 BitmapFactory.Options 的 inPreferredConfig 选项,将存储方式设置为 Bitmap.Config.RGB_565。这种存储方式一个像素占用 2 个字节,所以最终占用内存直接减半。如下:

打印日志如下:

2020-05-23 14:37:06.213 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:37:07.047 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200 width:1920 allocationByteCount:4608000 // 相比9216000减少一半的内存 byteCount:4608000 rowBytes:3840 density:240 mutable:false

这个结论我们在介绍Bitmap 的 Config时候已经介绍过了,这里不多说了;

另外 Options 中还有一个 inSampleSize 参数,可以实现 Bitmap 采样压缩,这个参数的含义是宽高维度上每隔 inSampleSize 个像素进行一次采集。比如以下代码:

因为宽高都会进行采样,所以最终图片会被缩略 4 倍,最终打印效果如下:

2020-05-23 14:42:59.440 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:2402020-05-23 14:43:00.332 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAAA: height:600  width:960  allocationByteCount:2304000 // 为9216000的1/4,极大降低了内存 byteCount:2304000  rowBytes:3840  density:240  mutable:false

Bitmap 复用

场景描述

如果在 Android 某个页面创建很多个 Bitmap,比如有两张图片 A 和 B,通过点击某一按钮需要在 ImageView 上切换显示这两张图片,

可以使用以下代码实现上述效果:

但是在每次调用 switchImage 切换图片时,都需要通过 BitmapFactory 创建一个新的 Bitmap 对象。当方法执行完毕后,这个 Bitmap 又会被 GC 回收,这就造成不断地创建和销毁比较大的内存对象,从而导致频繁 GC(或者叫内存抖动)。像 Android App 这种面相最终用户交互的产品,如果因为频繁的 GC 造成 UI 界面卡顿,还是会影响到用户体验的。可以在 Android Studio Profiler 中查看内存情况,多次切换图片后,显示的效果如下:

  • 使用 Options.inBitmap 优化

实际上经过第一次显示之后,内存中已经存在了一个 Bitmap 对象。每次切换图片只是显示的内容不一样,我们可以重复利用已经占用内存的 Bitmap 空间,这个概念上面也讲过,这里具体做法就是使用 Options.inBitmap 参数。修改如下:

解释说明:

第一个红框处创建一个可以用来复用的 Bitmap 对象。第二处红框,将 options.inBitmap 赋值为之前创建的bitmap 对象,从而避免重新分配内存。重新运行代码,并查看 Profiler 中的内存情况,可以发现不管我们切换图片多少次,内存占用基本处于一个水平线状态。

我们再来看日志:

2020-05-23 15:33:46.515 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA: height:1200  width:1920  allocationByteCount:9216000  byteCount:9216000  rowBytes:7680  density:240  mutable:true 2020-05-23 15:34:09.031 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA: height:322  width:640  allocationByteCount:9216000  byteCount:824320  rowBytes:2560  density:240  mutable:true

第二张图片内存明显复用了第一张Bitmap内存大小 9216000,而第二张图片byteCount大小为824320,而不等于allocationByteCoung大小,就在文章开头我们也讲解过getAllocationByteCoun和getByteCount的区别,很好的解释了这个结果;

但是这里需要注意,我们第一张Bitmap比第二张Bitmap大,如果第一张Bitmap比第二章小的话,这里就不能复用了,前面我们也是提到过的,否则会直接崩溃掉, 如下:

我们默认在onResume里面 imageView?.setImageBitmap(bitmap) 此时这个bitmap是上图image【1】对应的Bitmap,他的内存分配上面也打印过为 824320 ,然后点击切换时候我们就复用这个Bitmap内存,将image【0】内容再填充到这个bitmap中,我们试着运行一下结果发现

直接给我们抛出异常了, 我们在decordResource源码中找到答案了,如下

如果bm为空,而且开启了bitmap复用,这里就会崩掉。。。

这是因为 Bitmap 的复用有一定的限制:

在 Android 4.4 版本之前,只能重用相同大小的 Bitmap 内存区域, 4.4 之后你可以重用任何 Bitmap 的内存区域,只要这块内存比将要分配内存的 bitmap 大就可以。

那么我们需要做一下处理了,如下

第一步:先初始化一个bitmap,这个bitmap我用的是bitmap[1]中的 加载到内存后的大小为824320 ;

第二步:取第一张图片也就是image[0],实际内存大小为9216000,由于我们把inJustDecodeBound=true 此时并没有正真加载到内存中,为了获取该bitmap配置信息;

第三步:判断bitmap 能否复用, 方法如下

获取option中的预加载bitmap的大小,然后根据位图存储格式计算预加载的bitmap大小,最后返回比较结果, 这里默认采用ARGB_8888所以??4;

如果预加载的bitmap所占内存大小<=被复用bitmap大小,

option.InMutable=true;option.InBitmap=bitmap

最后一步:再次加载bitmap并实现复用;

细心的你可能也发现了在每次加载之前,除了 inBitmap 参数之外,我还将 Options.inMutable 置为 true,这里如果不置为 true 的话,BitmapFactory 将不会重复利用 Bitmap 内存,并输出相应 warning 日志:

W/BitmapFactory: Unable to reuse an immutable bitmap as an image decoder target

Bitmap 缓存

当需要在界面上同时展示一大堆图片的时候,比如 ListView、RecyclerView 等,由于用户不断地上下滑动,某个 Bitmap 可能会被短时间内加载并销毁多次。这种情况下通过使用适当的缓存,可以有效地减缓 GC 频率保证图片加载效率,提高界面的响应速度和流畅性。

最常用的缓存方式就是 LruCache,基本使用方式如下:

解释说明:

图中 指定 LruCache 的最大空间为 20M,当超过 20M 时,LruCache 会根据内部缓存策略将多余 Bitmap 移除。

图中 sizeOf 方法指定了插入 Bitmap 时的大小,当我们向 LruCache 中插入数据时,LruCache 并不知道每一个对象会占用大多内存,因此需要我们手动指定,并且根据缓存数据的类型不同也会有不同的计算方式。

最后就是我们存取操作了。

上面就是今天的内容,讲解类Bitmap的相关基础知识点和优化,Bitmap实际问题的处理远不止这么多,像截屏长图的处理,如果不处理这张”超大图“,应用很容易就崩掉,这里需要用到分片加载, 这里不多说了,大家可自行查阅官方文档学习一下。

  • 程序员如何在大环境恶劣下脱颖而出?(3000字干货)

  • 国内第一创作平台大佬,教你如何写好一篇技术博客?

  • git命令的进阶和复习(带动图效果)

图片的实际问题还遇到过哪些么?

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码