android加载大量图片内存优化,Android图片加载内存优化
利用BitmapFactory.Options实现图片内存优化
通过设置options.inPreferredConfig控制内存占用
首先准备了一张1280x800的blue_bg.png图片,我们知道这张图片加载到内存默认占用的大小是1280x800x4 = 4096000byte
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg);
// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节
// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());
}
我们知道可以通过设置options.inPreferredConfig,来设置图片加载时候占用的字节ALPHA_8(1byte),RGB_565(2),ARGB_4444(2),ARGB_8888(4)。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());
}
经过日志分析,发现并没有生效,占用的内存并没有减少一半。大概的原因是
设备加载图片时候,不同android版本对图片编解码的支持不一样
图片的格式也决定了他不能支持通过Bitmap.Config.RGB_565模式去加载,所以会选择默认ARGB_8888模式去加载,所以我们的内存占用并没有减少。
这篇文章会有详细的介绍:文章链接
通过设置不同的inPreferredConfig值真的能减少Bitmap加载时占用的内存么?链接
我们增加一张one.jpg图片做对比,具体代码如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
// options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);
// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节
// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());
}
Bitmap one = BitmapFactory.decodeResource(getResources(), R.drawable.one,options);
// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节
// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d("zyl--","one = " + one.getAllocationByteCount() +", ="+one.getByteCount());
}
通过日志我们发现,jpg图片对于设置inPreferredConfig属性生效了,内存占用减少了一半。进一步证实了图片格式对于inPreferredConfig属性是否生效有一定影响。
通过设置采样率options.inSampleSize来减少图片内存占用
通过设置采样率options.inSampleSize来减少图片内存占用问题。inSampleSize 参数,可以实现 Bitmap 采样压缩,这个参数的含义是宽高维度上每隔 inSampleSize 个像素进行一次采集。
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_bg,options);
// 默认情况下 BitmapFactory 使用 Bitmap.Config.ARGB_8888 的存储方式来加载图片内容,而在这种存储模式下,每一个像素需要占用 4 个字节
// 1280x800x4 = 4096000 byte 核算大约4000kb = 4M
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.d("zyl--","bitmap = " + bitmap.getAllocationByteCount() +", ="+bitmap.getByteCount());
}
通过日志分析,得到当采样率为2的时候,图片占用内存为
: bitmap = 1024000,
内存减少了1/4。
通过设置 Options.inBitmap,使Bitmap 对象重复使用,节省内存
实现点击切换图片的功能,代码如下:
public class MainActivity extends AppCompatActivity {
private int resIndex;
int[] resIds = {R.drawable.one, R.drawable.two};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView hello = findViewById(R.id.hello);
final ImageView iv = findViewById(R.id.iv);
hello.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
iv.setImageBitmap(getBitmap());
}
});
reuseBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.one);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private Bitmap getBitmap() {
return BitmapFactory.decodeResource(getResources(), resIds[resIndex++ % 2], options);
}
以上代码运行后,发现当我们切换图片时,内存情况如图:
内存抖动
每次切换图片都需要通过 BitmapFactory 创建一个新的 Bitmap 对象。当方法执行完毕后,这个 Bitmap 又会被 GC 回收,这就造成不断地创建和销毁比较大的内存对象,从而导致频繁 GC(或者叫内存抖动)。像 Android App 这种面相最终用户交互的产品,如果因为频繁的 GC 造成 UI 界面卡顿,还是会影响到用户体验的。可以在 Android Studio Profiler 中查看内存情况。
使用Options.inBitmap,重复利用已经占用内存的 Bitmap 空间,解决内存抖动问题。
public class MainActivity extends AppCompatActivity {
private int resIndex;
int[] resIds = {R.drawable.one, R.drawable.two};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView hello = findViewById(R.id.hello);
final ImageView iv = findViewById(R.id.iv);
hello.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
// Intent intent = new Intent(MainActivity.this,MyActivity.class);
// startActivity(intent);
iv.setImageBitmap(getBitmap());
}
});
BitmapFactory.Options options = new BitmapFactory.Options();
//设置为true,使这块内存能够复用
options.inMutable =true;
reuseBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.one,options);
}
/**
* 重用bitmap
*/
private Bitmap reuseBitmap;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private Bitmap getBitmap() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
//为true 只解析bitmap的占用内存大小,不加载bitmap到内存中
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), resIds[resIndex % 2], options);
if (canUseForInBitmap(reuseBitmap, options)) {
options.inMutable = true;
options.inBitmap = reuseBitmap;
}
//恢复设置
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), resIds[resIndex++ % 2], options);
}
static boolean canUseForInBitmap(
Bitmap candidate, BitmapFactory.Options targetOptions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
// the new bitmap is smaller than the reusable bitmap candidate
// allocation byte count.
int width = targetOptions.outWidth / targetOptions.inSampleSize;
int height = targetOptions.outHeight / targetOptions.inSampleSize;
int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
return byteCount <= candidate.getAllocationByteCount();
}
// On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
return candidate.getWidth() == targetOptions.outWidth
&& candidate.getHeight() == targetOptions.outHeight
&& targetOptions.inSampleSize == 1;
}
/**
* A helper function to return the byte usage per pixel of a bitmap based on its configuration.
*/
static int getBytesPerPixel(Bitmap.Config config) {
if (config == Bitmap.Config.ARGB_8888) {
return 4;
} else if (config == Bitmap.Config.RGB_565) {
return 2;
} else if (config == Bitmap.Config.ARGB_4444) {
return 2;
} else if (config == Bitmap.Config.ALPHA_8) {
return 1;
}
return 1;
}
}
运行后发现内存抖动的问题解决了:
内存图
android加载大量图片内存优化,Android图片加载内存优化相关推荐
- Android踩坑日记:使用Fesco图片加载库在GridView上的卡顿优化
1,fresco是一个强大的图片加载库 2,fresco设计了一个叫做image pipeline(图片管道)的模块,它负责从从网络,从本地文件系统,从本地资源加载图片,为了最大限度节约资源和cpu时 ...
- ios 图片加载内存尺寸_iOS内存分析上-图片加载内存分析
简介 对于大多数App来说,内存占用主要就是图片.本文将从实用的角度分析,iOS图片的内存占用.测量.优化等. iOS内存-有什么影响 在移动操作系统设备中,是不能像PC一样进行内存swap的,而随着 ...
- WEEX-EEUI 卡顿优化(图片加载)
前言 前两天前端的小伙伴告诉我,他写的页面再android手机上特别的卡,崩溃了很多次.这让我紧张了起来,已经用weex-eeui做过一个项目了,之前还没发现这个问题.因为问题不好定位,让我想起了之前 ...
- web资源优化之图片加载的时机
前言 图片资源是WEB项目中很重要的组成部分,也是交互设计中一个很重要的体现,往往一张图能胜过千言万语,所谓"一图胜白言"就是这个道理.然后大量的图片也会给服务器带来很大的压力,据 ...
- html加载时页面闪烁白色背景,解决页面加载闪白问题-背景图片加载优化
页面加载闪白 今天遇到一个问题,写了一个使用深色背景图的网页,发现访问/刷新时,会出现短暂的闪白现象. 之前使用浅色背景时没有发现过这个问题,搜索半天也没有找到特别直白有效的回答. 找到的几个答案,有 ...
- ios 加载大量图片崩溃_iOS超高分辨率图片崩溃解决方法
本文为CocoaChina网友xxg90s投稿 前言: ID作为一款以IM为基础的办公软件,在用户使用过程中,经常会遇到一些超大的或者超高分辨率的图片(以下统一称:大图).基于SDWebImage为基 ...
- 如何给图片加水印?这三个图片加水印方法,帮你添加花式水印
大家有没有这样情况,自己好不容易做好的作品发布到网络平台上结果被他人盗用了,很让人气愤,那我们应该如何避免这种情况呢? 其实我们可以给自己的作品加上一些专属的个性化水印,那小伙伴们知道图片加水印怎么操 ...
- java 图片加水印不失真_java实现图片加水印效果
图片加水印代码,这些代码不常用,但是用到的时候需要注意的地方也挺多的,每次都重写比较麻烦,记下来备忘.代码是图片加水印的一般流程,可根据实际项目需要自行修改. 注:代码在JPG和PNG格式图片下测试通 ...
- 如何给图片加水印?分享怎么给图片加水印的方法
当我们在平台上发布自己精心拍摄的照片,或分享自己总结的知识点时,难免会遇到一些人盗用自己图片的情况.这时候,我们就会给图片添加上水印,从而来防止自己的图片被盗,那要怎么给图片加水印呢?别着急,今天我就 ...
- html给图片加个框,PS怎么给图片加边框 几个步骤搞定
给自己的的照片弄个精美的边框是非常常见的操作,那么在制作电子相册的过程中,如何用PS来给你的照片加一个好看的边框呢,其实不难,下面就来说一下操作方法,有兴趣的朋友来参考学习下吧. 类别:图像处理 ...
最新文章
- uva 101 The Blocks Problem
- 目标检测--边界框(bounding box)解析
- linux-安装jdk
- tomcat日志按天切分
- Android NDK工程创建与编译运行
- 【包邮送书活动】20210924期-开奖通知
- JAVA中自己写的util中的chop,Java StringUtils.chop方法代碼示例
- 通过WiFi控制智能小车机器人制作过程详解
- (37)FPGA原语设计(BUFR)
- mysql 设置默认值_为什么 Flink 无法实时写入 MySQL?
- QT 设置QDialog显示与隐藏系统按钮(最大小化等)
- 基模高斯光束matlab_MATLAB 高斯光束传播轨迹的模拟
- Windows实例通过IIS如何搭建多个FTP站点
- chd mysql 作用_Cloudera Manager CHD 切换内置数据库PostgreSQL 到 Mysql
- php人人商城定时任务,人人商城异步操作菜单示例
- 12306火车票助手
- 什么轴的机械键盘声音小
- el-radio-button 设置默认选中问题
- excel函数获取长域名的顶级域名
- 世界杯数据可视化分析