本文主要讲解说明图片和apk安装文件获取缩略图的实现方式
1. 图片获取缩略图
思想: 主要根据BitmapFactory类为基础实现;
主要涉及到了 BitmapFactory.Options类对象及 BitmapFactory.decodeFile(String, Options)方法
Options类用到的主要成员:
1. public boolean inJustDecodeBounds;
/**
* If set to true, the decoder will return null (no bitmap), but
* the out... fields will still be set, allowing the caller to query
* the bitmap without having to allocate the memory for its pixels.
*/
true -- 将不会返回实际的Bitmap对象(不会分配内存空间,避免内存溢出OOM),但是,可以获取图片的原始高度opt.outHeight、原始宽度信息opt.outWidth。
false - 将申请分配内存,获取得到Bitmap对象。
2. public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
/**
* If this is non-null, the decoder will try to decode into this
* internal configuration. If it is null, or the request cannot be met,
* the decoder will try to pick the best matching config based on the
* system's screen depth, and characteristics of the original image such
* as if it has per-pixel alpha (requiring a config that also does).
*
* Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
* default.
*/
表示图片解码时使用的颜色模式,也就是图片中每个像素颜色的表示方式。
参数inpreferredconfig的可选值有四个,分别为ALPHA_8,RGB_565,ARGB_4444,ARGB_8888。它们的含义列举如下。
参数取值 | 含义 |
---|---|
ALPHA_8 | 图片中每个像素用一个字节(8位)存储,该字节存储的是图片8位的透明度值 |
RGB_565 | 图片中每个像素用两个字节(16位)存储,两个字节中高5位表示红色通道,中间6位表示绿色通道,低5位表示蓝色通道 |
ARGB_4444 | 图片中每个像素用两个字节(16位)存储,Alpha,R,G,B四个通道每个通道用4位表示 |
ARGB_8888 | 图片中每个像素用四个字节(32位)存储,Alpha,R,G,B四个通道每个通道用8位表示 |
3. public int inSampleSize;
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
默认或最小值为1,一般大于1,为2的幂,若不为2的幂,解码器将会解码为最接近的2的幂。
该参数设置图片的缩放大小(压缩比),优化可以参考:http://www.jianshu.com/p/f15cd2ed6ec0
4. public boolean inPurgeable;
true -- 系统内存不足时,可以被回收
false -- 不能被回收
5. public boolean inInputShareable;
/**
* @deprecated As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this is
* ignored.
*
* In {@link android.os.Build.VERSION_CODES#KITKAT} and below, this
* field works in conjuction with inPurgeable. If inPurgeable is false,
* then this field is ignored. If inPurgeable is true, then this field
* determines whether the bitmap can share a reference to the input
* data (inputstream, array, etc.) or if it must make a deep copy.
*/
设置是否深拷贝,与 inPurgeable 搭配使用。
inPurgeable - false ,该参数没有意义,被忽略。
inPurgeable - true ,该参数为true,可以共享一个引用给输入数据(流、数组等);false-深拷贝
结合以上几个Options成员变量的使用,可以在获取缩略图时,避免OOM,当然,具体也可以参考:
http://blog.csdn.net/berber78/article/details/19830173
如何处理图片来避免OOM异常:
1.在Android 2.3.3以及之前,建议使用Bitmap.recycle()方法,及时释放资源。
2.设置Options.inPreferredConfig值来降低内存消耗 //如把默认值ARGB_8888改为RGB_565,节约一半内存
3.设置Options.inSampleSize 对大图片进行压缩
4.设置Options.inPurgeable和inInputShareable:让系统能及时回收内存。
1)inPurgeable:设置为True时,表示系统内存不足时可以被回 收,设置为False时,表示不能被回收。
2)inInputShareable:设置是否深拷贝,与inPurgeable结合使用,inPurgeable为false时,该参数无意义True: share a reference to the input data(inputStream, array,etc) 。 False :a deep copy。
5.使用decodeStream代替其他decodeResource,setImageResource,setImageBitmap等方法:
//decodeStream直接调用 JNI>>nativeDecodeAsset()来完成decode,无需再使用Java层的createBitmap,也不使用java空间进行分辨率适配,虽节省dalvik内存,但需要在hdpi和mdpi,ldpi中配置相应的 图片资源,否则在不同分辨率机器上都是同样大小(像素点数量)。其他方法如setImageBitmap、setImageResource、 BitmapFactory.decodeResource在完成decode后,最终都是通过java层的 createBitmap来完成的,需要消耗更多内存。
6.通过程序设定手动干涉GC处理,增强堆内存处理效率,在程序onCreate时:
private final static floatTARGET_HEAP_UTILIZATION = 0.75f;
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
7.自定义堆内存大小:
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
8.基础类型上,因为Java没有实际的指针,在敏感运算方面还是要借助NDK来完成。这点比较有意思的是Google 推出NDK可能是帮助游戏开发人员,比如OpenGL ES的支持有明显的改观,本地代码操作图形界面是很必要的;
--------------------------------------------------------------------------------------------------------------
了解了获取缩略图需要知道的一些前提信息后,下面简单说明步骤:
1. 创建Options对象,设置参数
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
2. 解密图片文件,获取信息
// 定义的ImageView尺寸
int thumbSize = context.getResources().getDimensionPixelSize(
R.dimen.list_item_thumbnail_size);
Point mThumbSize = new Point(thumbSize, thumbSize);
final int widthSample = opt.outWidth / mThumbSize.x;
final int heightSample = opt.outHeight / mThumbSize.y;
// 计算缩放比
int sampleSize = Math.min(widthSample, heightSample);
if (sampleSize<=0) sampleSize=1;
以上是获取 sampleSize的一种方式,可以有多种优化方式,其中,图片的宽和高可以用 sampleSize、opt.outHeight表示。
3. 赋值缩放比
opt.inSampleSize = sampleSize;
上面 第 2 步只是一种获取的方式,也可以有其他优化方式。
http://www.jianshu.com/p/f15cd2ed6ec0
4. 设置 缩略图获取所需参数
opt.inJustDecodeBounds = false;
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
5. 重新读出缩略图
// 读取缩放后的Bitmap
bmp = BitmapFactory.decodeFile(filePath, opt);
// 创建缩略图
bmp = ThumbnailUtils.extractThumbnail(bmp, mThumbSize.x,
mThumbSize.y,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
了解以上步骤后,可以参考如下类似的实现:
private static final int MAX_FILENAME_LENGTH = 50;
private static final int MAX_IMAGE_SIZE = 20 * 1024 * 1024;
//private static final int MAX_IMAGE_RESOLUTION = 4096 * 4096;
private static final int MAX_IMAGE_WIDTH = 4096;
private static final int MAX_IMAGE_HEIGTH = 4096;
public static final int MAX_NUM_COMPRESSION = 5;
// 获取缩略图接口
public static Bitmap readBitMap(String filePath, boolean isImage,
Context context) {
if (isImage) {
File tempFile = new File(filePath);
if (tempFile.length() > MAX_IMAGE_SIZE){
// 返回默认图片
getDefaultBitmap(context);
}
int thumbSize = context.getResources().getDimensionPixelSize(
R.dimen.list_item_thumbnail_size);
Point mThumbSize = new Point(thumbSize, thumbSize);
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, opt);
final int widthSample = opt.outWidth / mThumbSize.x;
final int heightSample = opt.outHeight / mThumbSize.y;
Log.d(TAG, "readBitMap() widthSample = " + widthSample+ " heightSample= "+heightSample
+" x="+ mThumbSize.x+" y="+ mThumbSize.y);
opt.inJustDecodeBounds = false;
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
Bitmap bmp = null;
boolean noBitmap = true;
boolean wbmp = filePath.endsWith(".wbmp");
int sampleSize = computeSampleSize(opt, -1, 128*128);
// 多种优化计算缩放比
// int sampleSize = Math.min(widthSample, heightSample);
int num_tries = 0;
if (wbmp && (opt.outWidth > MAX_IMAGE_WIDTH || opt.outHeight > MAX_IMAGE_HEIGTH)) {
return getDefaultBitmap(context);
}
while (noBitmap) {
try {
Log.d(TAG, "readBitMap().try: num_tries = " + num_tries + "; filePath = " + filePath +" sampleSize="+sampleSize);
opt.inSampleSize = sampleSize;
bmp = BitmapFactory.decodeFile(filePath, opt);
bmp = ThumbnailUtils.extractThumbnail(bmp, mThumbSize.x, mThumbSize.y,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
noBitmap = false;
} catch (OutOfMemoryError error) {
Log.e(TAG, "Happen OOM ", error);
if (++num_tries >= MAX_NUM_COMPRESSION) {
noBitmap = false;
}
if (bmp != null) {
bmp.recycle();
bmp = null;
}
System.gc();
sampleSize *= 2;
Log.d(TAG, "readBitMap().catch: num_tries = " + num_tries + "; filePath = " + filePath);
}
}
return bmp;
} else {
PackageManager pm = context.getPackageManager();
PackageInfo info = pm.getPackageArchiveInfo(filePath,
PackageManager.GET_ACTIVITIES);
Drawable drawable;
if (info != null) {
ApplicationInfo appInfo = info.applicationInfo;
appInfo.sourceDir = filePath;
appInfo.publicSourceDir = filePath;
drawable = appInfo.loadIcon(pm);
if (drawable instanceof NinePatchDrawable) {
Bitmap bitmap = Bitmap.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
} else if (drawable instanceof BitmapDrawable) {
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
} else {
drawable = null;
return null;
}
} else {
drawable = null;
return null;
}
}
}
public static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels);
int roundedSize;
if (initialSize <= 8 ) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 :
(int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 :
(int) Math.min(Math.floor(w / minSideLength),
Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}