百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

如何理解图片采样,这应该算是基础知识吧?

lipiwang 2024-10-15 18:45 29 浏览 0 评论

前言

最近线上有用户反馈在App使用过程中遇到大图的时,App异常的卡顿,甚至会出现崩溃的情况。后来排查了一番,发现一个同事在处理图片时,直接原图加载没有做任何“压缩”。这个case的出现,也就引出了这篇文章的必要性。

咱们日常开发过程中,都会使用各种各样的图片库比如Glide。由于所有图片操作都是一股脑的交给图片库去处理,所以即使在遇到大图加载的时候,也无法“复现”这类问题。

因为主流的图片库都帮咱们对大图进行了处理(正印证了那句话:当你能轻松进去的时候,你就该明白,不是你厉害,只是有人在前面替你开路——“鲁讯”)。

既然话都说开了,咱们作为新时代下的福报程序员,那就必须要在这条路上探探深浅。其实图片压缩的方式有很多种,今天咱们只要一种,那就是Google原生的高效加载大图的方案。

正文

进行压缩之前,咱们先来感受一下不压缩会怎样...

一、不压缩,直接加载大图

我随便new了一下项目,搞了一个这样的图:

其实也不是特别大,就是一张1080P的图。

然后随便的用一个ImageView去加载一下:

iv.setImageResource(R.drawable.test)

当我尝试run的时候,我高估了我的测试机....没有加载出来,就直接崩了。Logcat也是够直接,无情吐槽:

这么一张图,一共需要132710400Bytes的内存,也就是132m....等等,不对?!分辨率1080 * 1920的图片怎么可能会使用100+m的内存?

我们都知道,正常一个图片被加载到内存里的文件大小 = 图片分辨率的宽 * 图片分辨率的高 * 色彩格式。带入这个公式内存大小 = 1080 * 1920 * 4 = 7.9m,绝不可能是100+m这么多!

这里可能有朋友会有疑问,为啥JPEG的格式会4,JPEG格式没有alpha通道,不应该占这么大的空间。其实具体几,还是需要看这张图最终Bitmap.Config解出来的值,我这张图解出来是ARGB_8888,所以还是要*4。

如果你也有这个疑问,那么接下来的内容你要好好看咯。这个知识点恐怕是盲区...

二、番外:drawble、drawble-xxhdpi有什么区别

作为一个番外的内容部分。这一章节其实和图片压缩没有什么关系,只是额外聊一聊drawble这个文件夹

上述问题的根本原因就是在于文件放置的位置,我只在drawble文件夹下放置了图片资源。

所以...这种case下,如果加载这个资源的手机是一个高密度屏幕,那么这张图片被展示时,并非1080 * 1920...

接下来咱们来看一看,为什么资源文件随便放会带来这么大的问题!(以下内容,部分来自于官方文档)

文档中提到,如果资源提供不当,会导致缩放失真...。这里为什么系统要进行缩放其实也很好理解:

  • 对于系统来说,如果它向下(低密度)才找到需要引用的资源文件,那么最佳的策略便是将找到的图片资源整体放大。因为那里的图,预期是给低分辨率手机准备的。
  • 那么同理,如果系统向上(高密度)找到了需要引用的资源文件,那么缩小无疑是最佳的选择。因为那里的图,预期是给高分辨率手机准备的。

所以基于此,上述中OOM的内存值132710400bytes是这么算出来的:1080 * 4(这个4是手机dpi640 / 资源dpi160 所得) * 1920 * 4 * 4

小贴士:dpi = 手机分辨率长宽各自平方之和开方,除以对角线长度(单位英寸)。 当然我们也可以通过api:resources.displayMetrics.xdpi。这里得到的值就基本等于当前手机的dpi


所以,强制加载这么大的一张图,是不是不负责任!这么大,硬往里塞,搁谁谁受得了?

三、Google提供的解决方案

既然咱们已经明确硬来是不行了,所以还是要采取一些技巧的。文章中开篇就道出了问题的所在:

Images come in all shapes and sizes. In many cases they are larger than required for a typical application user interface (UI). For example, the system Gallery application displays photos taken using your Android devices's camera which are typically much higher resolution than the screen density of your device.

Given that you are working with limited memory, ideally you only want to load a lower resolution version in memory. The lower resolution version should match the size of the UI component that displays it. An image with a higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.

简单翻译一下就是:太大就不要硬塞,缩到合适的尺寸再塞

文档里还有比较有意思的一句话:There are several libraries that follow best practices for loading images. You can use these libraries in your app to load images in the most optimized manner. We recommend the Glide

官方推荐,最为致命

其实文档中直接贴出了可以Ctrl +C/V就能使用的代码:

imageView.setImageBitmap(    decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100))fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {    // Raw height and width of image    val (height: Int, width: Int) = options.run { outHeight to outWidth }    var inSampleSize = 1    if (height > reqHeight || width > reqWidth) {        val halfHeight: Int = height / 2        val halfWidth: Int = width / 2        // Calculate the largest inSampleSize value that is a power of 2 and keeps both        // height and width larger than the requested height and width.        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {            inSampleSize *= 2        }    }    return inSampleSize}fun decodeSampledBitmapFromResource(        res: Resources,        resId: Int,        reqWidth: Int,        reqHeight: Int): Bitmap {    // First decode with inJustDecodeBounds=true to check dimensions    return BitmapFactory.Options().run {        inJustDecodeBounds = true        BitmapFactory.decodeResource(res, resId, this)        // Calculate inSampleSize        inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)        // Decode bitmap with inSampleSize set        inJustDecodeBounds = false        BitmapFactory.decodeResource(res, resId, this)    }}

代码很好理解,就是将需要加载的图片,按目标所需的加载尺寸进行一次采样,通过采样的值进行等比缩放。

不过这里有一个有趣的细节:官方的代码里是将采样结果进行了 * 2 ( inSampleSize*=2)。当时通过实战我们会发现, inSampleSize并不一定要传2的幂,传3传5传其他也是有效果的。

文档中提到这么一句话:

Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.(以2的幂作为计算结果,是根据inSampleSize文档,解码器通过四舍五入到最接近的2的幂来使用最终值。)

按照文档的解释inSampleSize为2/3时,效果一样,毕竟3最接近2的幂的值还是2。当时事实跑起来会发现,2和3的结果并不一样:

当inSampleSize = 3时,图片长和宽就是比减少了3倍...所以真是不知道官网的葫芦里卖的什么药。

尾声

到这,该唠的基本也就唠完了...内容并不深奥,但也算是必备的知识点~

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: