ImageLoader 笔记
BitmapFactory
我们不能够通过构造函数创建Bitmap
对象。如果需要将图片转成Bitmap
对象加载到内存中,就需要使用BitmapFactory
类。BitmapFactory
跟据图片数据源的不同,提供了几类获取Bitmap
的方法。如下:
数据源类型 | 方法 |
---|---|
byte[] | decodeByteArray(byte[] data, int offset, int length,BitmapFactory.Options opts) |
byte[] | decodeByteArray(byte[] data, int offset, int length) |
File | decodeFile(String pathName, BitmapFactory.Options opts) |
File | decodeFile(String pathName) |
FileDescriptor | decodeFileDescriptor(FileDescriptor fd) |
FileDescriptor | decodeFileDescriptor(FileDescriptor fd, BitmapFactory.Options opts) |
Resource | decodeResource(Resource res, int id) |
Resource | decodeResource(Resource res, int id, BitmapFactory.Options opts) |
ResourceStream | decodeResourceStream(Resource res, TypedValue value,InputStream is,Rect pad,BitmapFactory.Options opts) |
Stream | decodeStream(InputStream is) |
Stream | decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) |
BitmapFactory.Options
从上面的表格可以看出,每一类数据源的解码方法都有两个。其中一个都有一个BitmapFactory.Options
参数。这个参数对解码进行了配置。
它的可选参数如下:
参数 | 作用 |
---|---|
inBitmap : Bitmap | 重用一个Bitmap对象 |
inDensity : int | 这张图片解码使用的屏幕密度 |
inDither : boolean | deprecated in api-24, 如果设置改选项,那么解码的时候会尝试防抖动处理 |
inInputShareable : boolean | deprecated in api-21 |
inJustDecodeBounds : boolean | 如果设置该选项,返回值为null。但是可以从Options对象中获取Bitmap的宽高 |
inMutable : boolean | 如果设置,将会解码出一个可更改的Bitmap对象,而不是不可更改的 |
inPreferQualityOverSpeed : boolean | deprecated in api-24, 设置它会牺牲时间效率,提升图片的质量 |
inPreferredConfig : Bitmap.Config | 这里设置Bitmap的像素存储格式,也就是Bitmap的config对象 |
inPremutiplied : boolean | 默认为true,与dither类似是一种图像处理的方式 |
inPurgeable : boolean | deprecated in api-21 |
inSampleSize : int | 如果值大于1,那么生成一个缩略版的Bitmap |
inScaled : boolean | 如果设置为true,并且inDensity和inTargetDensity不一致的时候,那么生成的bitmap会按照inTargetDensity的密度缩放,而不是系统提供的密度 |
inScreenDensity : int | 屏幕的真实密度 |
inTargetDensity : int | 这张bitmap绘制的屏幕密度 |
inTempStorage : byte[] | 解码用的临时内存区域 |
mCancel : boolean | deprecated in api 24 |
outHeight : int | Bitmap的高度 |
outWidth : int | Bitmap的宽度 |
outMimeType: int | 解码图片的MimeType |
减少内存占用:
原理:
解码图片设置缩放比例可以减少Bitmap对象的内存占用。关键的参数是inSampleSize
参数。举个例子:
一张2018 x 1536 的图片如果完全解码为(ARGB_8888)的Bitmap,那么他的内存占用为 2048 * 1536 * 4=12M;
如果设置inSampleSize
为4,那么最终Bitmap对象的尺寸为512 x 384。内存占用为512 * 384 * 4 = 0.75M;
也就是inSampleSize = n
的时候,内存占用为1/(n * n)
n=1, 2, 4, 8, 16 .....
使用方法:
- 计算图片的尺寸
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
- 根据期望的
imageview
尺寸计算缩放的倍数
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = 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;
}
这里的imSampleSize的结果都是2的幂。根据inSampleSize的文档,如果传进去的inSampleSize非2的幂,那么会向下取2的幂为最终缩放比例。 例如:传 15 最终为 8;传 7最终为4;
- 根据
inSampleSize
解码图片
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
这里参考一些文章的思路,根据实验发现利用BitmapFactory.Options
中的Density
相关的设置也可以控制图像的大小:
如果单独设置inDensity
变量,那么只会影响到生成的Bitmap
的density的值。
如果想要更根据density
缩放,需要同时设置三个值:
变量 | 值 |
---|---|
inDensity | 图片数据对应的像素密度 |
inTargetDensity | 生成的bitmap的像素密度 |
inScale | 是否根据像素密度缩放,需要设置true |
测试代码:
// 这里使用了一张大小960000B大小的图片,放在assets目录下
// 这里的测试机默认屏幕像素密度480
AssetManager assetManager = getAssets();
InputStream is = null;
try {
is = assetManager.open("img_fjords.jpg");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTargetDensity = DisplayMetrics.DENSITY_HIGH; // 240
options.inDensity = DisplayMetrics.DENSITY_XXHIGH; // 480
options.inScaled = true;
Bitmap bitmap = BitmapFactory.decodeStream(is, new Rect(0, 0, 0, 0), options);
Log.d(LOG_TAG, "default density: " + 480);
Log.d(LOG_TAG, "default size: 960000");
Log.d(LOG_TAG, "bitmap density: " + String.valueOf(bitmap.getDensity()));
Log.d(LOG_TAG, "bitmap size: " + bitmap.getByteCount());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
输出结果:
08-20 03:03:06.528 7386-7386/com.example.densitytest D/MainActivity: default density: 480
08-20 03:03:06.528 7386-7386/com.example.densitytest D/MainActivity: default size: 960000
08-20 03:03:06.528 7386-7386/com.example.densitytest D/MainActivity: bitmap density: 240
08-20 03:03:06.528 7386-7386/com.example.densitytest D/MainActivity: bitmap size: 240000
*/
可以看出来,内存大小确实变化了。内存大小关系应该是 finalSize = originSize*(inTargetDensity/inDensity)^2
不过需要注意的是tagetSize如果与屏幕像素密度不一致的时候展示的时候还是会缩放。所以,在使用这个方法控制的内存的时候
通过inDensity来控制,这样就不需要额外修改bitmap的density。
Bitmap
这个类就代表位图,它的一部分接口如下:
方法 | 解释 |
---|---|
compress(Bitmap.CompressFormat format, int quality, OutputStream stream) : boolean | 把一个位图写入流中 |
copy(Bitmap.Config config, boolean isMutable) : Bitmap | 使用config配置复制一个Bitmap |
copyPixelsFromBuffer(Buffer src) : void | 从一个Buffer对象中复制出所有的像素 |
copyPixelsToBuffer(Buffer dst) : void | 将Bitmap的所有像素都复制到Buffer中 |
static createBitmap(Bitmap source, int x, int y, int width, int height) : Bitmap | 从已有的Bitmap对象中取一个子集 |
static createBitmap(int[] colors, int width, int height, Bitmap.Config config) : Bitmap | 根据颜色矩阵生成一幅位图 |
static createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config) | 返回一个可更改的Bitmap |
static createBitmap(int width, int height, Bitmap.Config config):Bitmap | 返回一个可更改的Bitmap |
static createScaledBitmap(Bitmap src, int dstWidth, int sdtHeight, boolean filter) | 创建一个缩放到指定尺寸的Bitmap |
describeContents() | ... |
eraseColor(int c) : void | 将bitmap的所有像素都设置成同一颜色 |
extractAlpha(): Bitmap | 生成一幅去掉Alpha值的Bitmap |
extractAlpha(Paint paint, int[] offsetXY) | . |
getAllocationByteCount(): int | 获取bitmap的尺寸 |
getByteCount() : int | 获取存储图片最少需要的空间 |
getConfig(): Bitmap.Config | 获取图片配置 |
getDensity() : int | 获取图片密度 |
getGenerationId() : int | 返回generationId |
getHeight() | 位图高度 |
getNinePatchChunck() : byte[] | 返回一个数组,为.9.png使用 |
getPixel(int x, int y): int | 获取具体位置的颜色值 |
getRowBytes() | 图片中一行像素占多少空间 |
getScaledHeight(int targetDensity) : int, getScaledWidth(int targetDenisty) : int | 特定目标屏幕密度下的高度 |
hasAlpha() : boolean | 如果每个像素都支持透明效果的话就返回true |
hasMipMap(): boolean | ... |
isMutable() : boolean | 是否可以更改 |
isPremultiplied() : boolean | 像素点是否是premulitplied格式存储 |
isRecycled() : boolean | 图片是否已经被回收 |
prepareToDraw() | 为绘制做缓存 |
reconfigure(int width, int height, Bitmap.Config config) : void | 更改Bitmap的配置属性,但是不会影响底层的存储 |
recycle() : void | 释放native层的对象,并释放对像素矩阵的引用 |
sameAs(Bitmap other) | 如果另一个Bitmap拥有同样的尺寸,配置,像素值就返回true |
setConfig(Bitmap.Config config): void | reconfig方法的一种捷径 |
setDensity(int density) : void | . |
setHasAlpha(boolean hasAlpha) : void | . |
setHasMipMap(boolean hasMipMap) : void | . |
set Height(int height):void | . |
setPixel(int x, int y):void | . |
setPremultiplied(boolean premultiplied) : void | . |
setWidth(int width):void | . |
writeToParcel(Parcel p, int flags): void | . |
Bitmap.Config
这个类是用来配置像素格式的。它决定了像素的大小,图像的质量
变量名 | 大小(B) | 补充说明 |
---|---|---|
ALPHA_8 | 1 | 只有黑白灰,就像黑白电视,最节省空间 |
ARGB_4444 | 2 | 由于图像质量问题,建议使用ARGB_8888。deprecated since api 14 |
ARGB_8888 | 4 | 最高画质,建议使用,空间使用最多 |
RGB_565 | 2 | 颜色相对丰富,适合不做透明处理的图像 |
Bitmap.CompressFormat
变量名 | 说明 |
---|---|
JPEG | 有损压缩,画质不稳定,存储传输效率高 |
PNG | 无损压缩,画质很好,存储传输效率低 |
WEBP | api 14 以后才提供使用,效果未知 |
这里对压缩做一下说明。compress
方法有三个参数:
第一个是格式,PNG格式是无损的,所以后面的第二个参数对它没有影响。另外两种格式都有影响。
第二个是压缩比,取值在0~100之间。数字越大图片质量越高,体积越大。100 代表不压缩,0代表尽全力压缩。
第三个是输出流。
reconfigure
方法,它是不更改底层像素值的。调用这个方法之后只是“看起来”变了,不会影响内存。
getAllocationByteCount()
与getByteCount()
分别需要API-19和API-12,api版本相对较高。事实上源码的计算很简单,如果app使用的时候受到api限制的话,完全可以自己计算:
public final int getBytesCount() {
return getRowBytes() * getHeight();
}
LruCache
基于LinkedHashMap
的一种经典的内存缓存模型。它是用强引用控制的缓存。可以设置缓存的大小,个数。可以统计命中率,读写次数。它是线程安全的。从做缓存的角度来说,要比WeakHashMap要好很多。
api 12 以上可以直接使用。api 12 以下可以通过support v4包 使用。
它的接口十分简单明了
具体的接口参见:LruCache
DiskLruCache
DiskLruCache
并不是谷歌官方的API。它是推荐给开发者使用的文件缓存的类。从名称上很好理解,文件系统中的Lru缓存。它的源码地址。
它的原理 利用LinkedHashMap在内存中记录文件缓存的最近访问顺序。磁盘中利用了journal文件作为日志文件,记录文件读写操作。每次创建DiskLruCache的时候都会通过journal日志重建LinkedHashMap的对象,这样在每次重新创建的时候也可以保持之前LRU的效果。源码不长,有兴趣的同学自行研究。网络上也有比较详细的介绍。
可以控制的变量:
- 缓存路径。建议选择App的cache目录下;
- cache版本。cache版本升级的时候会把旧的缓存全部清除;
- cache大小。cache的大小要小于缓存路径下的可有
- 日志条数。默认2000条。
ImageLoader 笔记的更多相关文章
- 《Android源码设计模式》学习笔记之ImageLoader
微信公众号:CodingAndroid cnblog:http://www.cnblogs.com/angel88/ CSDN:http://blog.csdn.net/xinpengfei521 需 ...
- 《android开发艺术探索》读书笔记(十二)--Bitmap的加载和Cache
接上篇<android开发艺术探索>读书笔记(十一)--Android的线程和线程池 No1: 目前比较常用的缓存策略是LruCache和DiskLruCache,LruCache常被用作 ...
- ImageLoader初始化以及调用
1.首先在当前程序的Application中调用ImageLoader的初始化init()方法 [java] view plain copy private void initImageLoader( ...
- Android开发笔记——以Volley图片加载、缓存、请求及展示为例理解Volley架构设计
Volley是由Google开源的.用于Android平台上的网络通信库.Volley通过优化Android的网络请求流程,形成了以Request-RequestQueue-Response为主线的网 ...
- qml自学笔记------自己写相似于劲舞团的按键小游戏(中)
接上篇<qml自学笔记------自己写类似于劲舞团的按键小游戏(上)> 第三部分DisplayPart.qml 代码的其它部分都是渣,就这里花了点时间,整个小游戏就靠这个文件. 首先,屏 ...
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
随机推荐
- a标签文字选中后的颜色样式更改
::selection 选择器,选择被用户选取的元素部分.是css3的用法,讲真,我觉得这个东西没必要特地去写.因为选中样式默认的会根据你的背景颜色还有字体color来设置颜色 这是我默认的样式
- LVDS/DVI/HDMI Interface
数字视频信号 以SXGA为例,其时序如下: 垂直: 水平: 图中DSPTMG为使能信号,VSYNC为场同步信号,HSYNC为行同步信号.在行场的消隐期(T1与T7),DSPTMG为低电 ...
- 阿里开源的热补丁框架AndFix使用教程
阿里巴巴推出的AndFix框架 首次给出大家这个框架的地址:https://github.com/alibaba/AndFix 对源码比较感兴趣的同学们可以自行研究代码 AndFix原理介绍 AndF ...
- 阿里巴巴开源前端框架--Weex实践
Weex是最近很火很NB的一个技术产品,因为本篇介绍的是怎样使用Weex的最佳实践,所以就不罗里吧嗦的夸它怎么怎么好了,感兴趣的可以访问Weex HomePage,或加入旺旺群:1330170019. ...
- CSS注释
CSS注释 1./*注释内容*/ /*-moz-background-origin:border; -webkit-background-origin:border; -moz-background- ...
- Java求素数时出现错误
Java求素数时出现错误 1.具体错误如下 No enclosing instance of type Prime is accessible. Must qualify the allocation ...
- Excel 2010高级应用-圆环图(七)
Excel 2010高级应用-圆环图(七) 基本操作如下: 1.新建空白Excel文档,并命名为圆环图 2.单击"插入",并找到圆环图图样 3.单击圆环图图样,并在空白文档上生成图 ...
- Error: expected expression, got '}'
1.错误描述 Error: expected expression, got '}' .globalEval/<@http://localhost:8080/Sys/resource/globa ...
- 石子归并 51Nod - 1021
N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价. 例如: 1 2 3 4,有 ...
- iOS - Quartz 2D 手势截屏绘制
1.绘制手势截屏 具体实现代码见 GitHub 源码 QExtension QTouchClipView.h @interface QTouchClipView : UIView /** * 创建手势 ...