位图
最常见的图片格式有两种,png和jpg。两者的区别:
-
png背景可以为透明,jpg背景不可以。类似于iconfont上任意素材下载下来都是png格式,它是没有背景的
2018-10-22_164454.png - png是无损压缩,jpg是有损压缩,会牺牲图片质量
一张位图的所占内存 = 图片的长度px * 宽度px * 像素点所占的内存
而像素点所占内存的大小跟色彩模式有关
Bitmap.Config | 字节 |
---|---|
Bitmap.Config ARGB_4444 | 即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 ,占2字节 |
Bitmap.Config ARGB_8888 | 即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位,占4字节 |
Bitmap.Config RGB_565 | 即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位,占2字节 |
Bitmap.Config ALPHA_8 | 每个像素占四位,只有透明度,没有颜色 A = 8。占1字节 |
A:透明度 R:红色 G:绿 B:蓝
android中创建的bitmap一般会选用ARGB8888模式。当图片的像素配置和手机配置不一样时,开启抖动可以避免图片显示过于失真
BitmapDrawable
并不是所有的drawable都有宽高,但是位图是有内部宽高的,可以通过getIntrinsicWidth和getIntrinsicHeight来获得。
- bitmap转drawable
Drawable drawable = new BitmapDrawable(getResources(),bitmap);
- drawable转bitmap
private void drawableToBitamp(Drawable drawable) {
BitmapDrawable bd = (BitmapDrawable) drawable;
bitmap = bd.getBitmap();
}
Bitmap的加载和压缩
BitmapFactory类提供了四个方法
- decodeFile(String pathName, Options opts)
- decodeResource(Resources res, int id, Options opts)
- decodeSteam(@Nullable InputStream is)
- decodeByteArray(byte[] data, int offset, int length, Options opts)
分别用于支持从文件系统,资源,输入流和字节数组中加载返回一个bitmap。但是加载图片在一定程度上容易引起OOM,需要提高bitmap加载时的性能。首先可以对图像压缩。
质量压缩
质量压缩主要借助Bitmap中的compress方法实现
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
format压缩后的格式,取值为Bitmap.CompressFormat .JPEG,Bitmap.CompressFormat.PNG,Bitmap.CompressFormat.WEBP;quality质量,0-100之间。值越小,经过压缩后图片失真越严重,当然图片文件也会越小。但是,质量压缩只是改变磁盘中的文件大小,并不能改变加载时内存中的图片大小
public static void compressQuality(Bitmap bitmap, int quality, File file) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 90, baos);// 质量压缩方法
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
return bitmap;
}
大小压缩
等比例缩放图片大小。磁盘中图片的大小并没有改变,只是改变了加载时内存中的图片大小
/**
* 尺寸压缩
*
* @param bitmap
* @param file
*/
public static void compressSize(Bitmap bitmap, File file) {
int ratio = 8;//尺寸压缩比例
Bitmap result = Bitmap.createBitmap(bitmap.getWidth() / ratio, bitmap.getHeight() / ratio, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bitmap.getWidth() / ratio, bitmap.getHeight() / ratio);
canvas.drawBitmap(bitmap, null, rect, null);
compressQuality(result, 100, file);
}
采样率压缩
通过BitmapFactory.Options来缩放图片,主要是inSampleSize参数,取值应该为2的指数,1,2,4,8,16。如果不是2的指数,则向下去整选择一个接近2的指数来代替
流程:
- inJustDecodeBounds 设置为true 并加载图片;为true时,并不会真正的加载图片,轻量级的操作
- 取出原图片的宽高 对应options的outHeight,outWidth
- 根据需求计算采样率
- inJustDecodeBounds 设置为false
public static void compressSample(String filePath, File file) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath,options);
options.inSampleSize = calcuateInSampleSize(options,reqwidth,reqheight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
}
其他
Android图片压缩的几种方案,里面有写ndk的压缩方案。
鲁班压缩 开源库,接近微信朋友圈压缩后的效果
缓存策略
一般是这样的"内存-本地-网络"三级缓存策略。常用的缓存算法是LRU,核心思想是当缓存满时,会优先淘汰近期最少使用的缓存对象。采用LRU算法的缓存有两种,LruCache用于实现内存缓存,DiskLruCache充当设备缓存。LruCache内部采用一个LinkedHashMap以强引用的形式来存储缓存,它是线程安全的,直接new对象,同时重写sizeof方法。而DiskLruCache提供open来创建自身
mDiskLruCache = DiskLruCache.open(file_File,version_int,1,cachesize_long)
缓存添加通过editor完成,获取图片url对应的key,根据key就可以获得editor对象
String key = hashKeyFromUrl(url);//可能有特殊字符,一般用md5值作为key
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
然后就可以得到一个文件输出流
OutputSteam steam = editor.newOutputSteam(DISK_CACHE_INDEX);
然后写入文件系统,提交。如果异常可以通过editor的abort回退操作
if(downloadUrlToSteam(url,steam)){
editor.commit();
}else{
editor.abort();
}
mDiskLruCache.flush();
模糊
常用的图片高斯模糊技术:RenderScript是在Android3.0(API 11)引入的。而Android图片高斯模糊处理,通常也是用这个库来完成。它提供了我们Java层调用的API,实际上是在c/c++ 层来处理的,所以它的效率和性能通常是最高的。具体实现代码
private static Bitmap rsBlur(Context context,Bitmap source,int radius){
Bitmap inputBmp = source;
//(1)创建RenderScript内核对象
RenderScript renderScript = RenderScript.create(context);
// Allocate memory for Renderscript to work with
//(2)创建一张渲染后的输入图片
/**
* 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
* 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
*/
final Allocation input = Allocation.createFromBitmap(renderScript,inputBmp);
final Allocation output = Allocation.createTyped(renderScript,input.getType());
//(3) 创建一个模糊效果的RenderScript的工具对象
// Load up an instance of the specific script that we want to use.
ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
//(4)设置ScriptIntrinsicBlur对象的输入内存
scriptIntrinsicBlur.setInput(input);
//(5) 输入模糊半径0-25
scriptIntrinsicBlur.setRadius(radius);
//(6) 启动内核,调用方法处理:调用forEach 方法模糊处理
scriptIntrinsicBlur.forEach(output);
//(7) 从Allocation 中拷贝数据
output.copyTo(inputBmp);
//(8) 销毁RenderScript对象
renderScript.destroy();
return inputBmp;
}
原图与模糊后效果
2018-10-29_145920.png
其余还有NativeBlur,还有JavaBlur直接在Java层做图片的模糊处理。对每个像素点应用高斯模糊计算、最后在合成Bitmap的方法,但是相对于NativeBlur以及RsBlur更耗时,这种方式是把图片全部加载到内存,如果图片较大,容易导致OOM。
可参考如何封装个好用的高斯模糊组件
内存大小
看了这篇文章后Android中一张图片占据的内存大小是如何计算,总结了一些知识点
- pic.png占用空间大小 并不等于加载后的内存。bitmap.getByteCount得到的值等于长高4B(系统默认是以 ARGB_8888)
- 位于 res 内的不同资源目录中的图片,当加载进内存时,会先经过一次分辨率的转换,然后再计算大小,宽高会改变,转换的影响因素是设备的 dpi 和不同的资源目录。所以图片需要正确的加载对应目录下的图片,否则非常影响内存的大小
- 同一图片,放在 res 内相同的资源目录下,但在不同 dpi 的设备中,图片占用的内存空间也是会不一样的
加载大图
对于图片加载还有种情况,就是单个图片非常巨大,并且还不允许压缩。比如显示:世界地图、清明上河图、微博长图等。通过局部加载,然后手势拖动查看其余部分。BitmapRegionDecoder可以用来显示图片的某一块矩形区域
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
bitmapRegionDecoder.decodeRegion(rect, options); //rect 为区域
然后自定义显示大图控件
- 提供一个设置图片的入口
- 重写onTouchEvent,在里面根据用户移动的手势,去更新显示区域的参数
- 每次更新区域参数后,调用invalidate,onDraw里面去regionDecoder.decodeRegion拿到bitmap,去draw
Android 高清加载巨图方案 拒绝压缩图片
其余https://mp.weixin.qq.com/s/4Ol-_PdO_yJwc4bIHqgzgg
网友评论