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. 整体架构图 ...
随机推荐
- NLP+词法系列(二)︱中文分词技术简述、深度学习分词实践(CIPS2016、超多案例)
摘录自:CIPS2016 中文信息处理报告<第一章 词法和句法分析研究进展.现状及趋势>P4 CIPS2016 中文信息处理报告下载链接:http://cips-upload.bj.bce ...
- NLP+词法系列(一)︱中文分词技术小结、几大分词引擎的介绍与比较
笔者想说:觉得英文与中文分词有很大的区别,毕竟中文的表达方式跟英语有很大区别,而且语言组合形式丰富,如果把国外的内容强行搬过来用,不一样是最好的.所以这边看到有几家大牛都在中文分词以及NLP上越走越远 ...
- Android Parcelable和Serializable的区别
本文主要介绍Parcelable和Serializable的作用.效率.区别及选择,关于Serializable的介绍见Java 序列化的高级认识. 1.作用 Serializable的作用是为了保存 ...
- 【php】windows安装PHP5.5+Apache2.4
php5.5和apache2.4的整合 看到php的版本升级了,就想试下新的特性 一.准备下载的文件 apache2.4.9 http://www.apachelounge.com/download/
- [2015-06-10 20:53:50 - Android SDK] Error when loading the SDK:
1.错误描述 [2015-06-10 20:53:50 - Android SDK] Error when loading the SDK: Error: Error parsing D:\Andro ...
- ubuntu14.04 64位 安装eclipse出错
1 错误描述 org.eclipse.m2e.logback.configuration: The org.eclipse.m2e.logback.configuration bundle was a ...
- Django学习-9-ORM多对多操作
创建多对多: 方式一:自定义关系表 class Host(models.Model): nid = mo ...
- 用yeoman搭建react画廊项目笔记
1.安装yeoman npm install yo -g yo --version //检测 yeoman版本,成功显示版本号,则安装成功 2.到yeoman官网 http://yeoman.io ...
- 【BZOJ1483】【HNOI2009】梦幻布丁(启发式合并,平衡树)
[BZOJ1483][HNOI2009]梦幻布丁 题面 题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1 ...
- Bzoj4805: 欧拉函数求和
好久没写杜教筛了 练练手AC量刷起 # include <bits/stdc++.h> # define RG register # define IL inline # define F ...