1.BitmapFactory提供了四类方法来加载Bitmap:decodeByteArray、decodeFile、decodeResource、decodeStream。其中,decodeFile和decodeResource间接调用了decodeStream方法,最终都是调用BitmapFactory的native方法。getAllocationByteCount()方法可以获取bitmap的字节大小。
2.Bitmap压缩一般采用下面思路:
public class BitmapUtils {
public static Bitmap decodeBitmapByResource(Resources res , int resId , int viewWidth , int viewHeight){
return decodeBitmapByResource(null,resId,viewWidth,viewHeight);
}
public static Bitmap decodeBitmapByResource(BitmapCache cache,Resources res , int resId , int viewWidth , int viewHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
dealOptions(cache,options,viewWidth,viewHeight);
Bitmap candidate = BitmapFactory.decodeResource(res,resId,options);
if(cache!=null) {
cache.setCandidate(candidate);
}
return candidate;
}
public static Bitmap decodeBitmapByFile( String filePath , int viewWidth , int viewHeight){
return decodeBitmapByFile(null,filePath,viewWidth,viewHeight);
}
public static Bitmap decodeBitmapByFile(BitmapCache cache , String filePath , int viewWidth , int viewHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath,options);
dealOptions(cache,options,viewWidth,viewHeight);
Bitmap candidate = BitmapFactory.decodeFile(filePath,options);
if(cache!=null) {
cache.setCandidate(candidate);
}
return candidate;
}
public static Bitmap decodeBitmapByBytes( byte[] bytes , int viewWidth , int viewHeight){
return decodeBitmapByBytes(null,bytes,viewWidth,viewHeight);
}
public static Bitmap decodeBitmapByBytes(BitmapCache cache , byte[] bytes , int viewWidth , int viewHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
dealOptions(cache,options,viewWidth,viewHeight);
Bitmap candidate = BitmapFactory.decodeByteArray(bytes,0,bytes.length,options);
if(cache!=null) {
cache.setCandidate(candidate);
}
return candidate;
}
public static Bitmap decodeBitmapByInputStream(InputStream inputStream , int viewWidth , int viewHeight){
return decodeBitmapByInputStream(null,inputStream,viewWidth,viewHeight);
}
public static Bitmap decodeBitmapByInputStream(BitmapCache cache , InputStream inputStream , int viewWidth , int viewHeight){
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
FileDescriptor fileDescriptor = ((FileInputStream)inputStream).getFD();
BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
dealOptions(cache,options,viewWidth,viewHeight);
Bitmap candidate = BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
if(cache!=null) {
cache.setCandidate(candidate);
}
return candidate;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void dealOptions(BitmapCache cache ,BitmapFactory.Options options,int viewWidth , int viewHeight){
if(canUseForInBitmap(cache,options)){
options.inMutable = true;
options.inBitmap = cache.getCandidate();
}else {
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;
int inSimpleSize = 1;
if (bitmapWidth > viewWidth || bitmapHeight > viewHeight) {
if (cache!=null && cache.isFixed()) {
int halfWidth = bitmapWidth / 2;
int halfHeight = bitmapHeight / 2;
while (halfWidth / inSimpleSize >= viewWidth &&
halfHeight / inSimpleSize >= viewHeight) {
inSimpleSize *= 2;
}
} else {
int widthRatio = bitmapWidth / viewWidth;
int heightRatio = bitmapHeight / viewHeight;
if (widthRatio >= 1 && heightRatio >= 1) {
inSimpleSize = Math.min(widthRatio, heightRatio);
}
}
}
options.inSampleSize = inSimpleSize;
}
options.inJustDecodeBounds = false;
}
public static boolean canUseForInBitmap(BitmapCache cache ,BitmapFactory.Options options){
if(cache==null || cache.getCandidate()==null) return false;
int width = options.outWidth / Math.max(options.inSampleSize,1);
int height = options.outHeight / Math.max(options.inSampleSize,1);
int byteCount = width * height * getBytePerPixel(cache.getCandidate().getConfig());
return byteCount <= cache.getCandidate().getAllocationByteCount();
}
public static int getBytePerPixel(Bitmap.Config config){
int bytePerPixel;
switch (config){
case ALPHA_8:
bytePerPixel = 1;
break;
case RGB_565:
case ARGB_4444:
bytePerPixel = 2;
break;
default:
bytePerPixel = 4;
break;
}
return bytePerPixel;
}
/*********** 获取分片图片 **************/
public static Bitmap getPieceBitMapByBytes(byte[] data,int offset, int length, Rect rect){
try {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(data,offset,length,false);
return getPieceBitMap(decoder,rect);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Bitmap getPieceBitMapByFileDescriptor(FileDescriptor fd, Rect rect){
try {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(fd,false);
return getPieceBitMap(decoder,rect);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Bitmap getPieceBitMapByPathName(String pathName, Rect rect){
try {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(pathName,false);
return getPieceBitMap(decoder,rect);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Bitmap getPieceBitMapByInputStream(InputStream inputStream, Rect rect){
try {
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream,false);
return getPieceBitMap(decoder,rect);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Bitmap getPieceBitMap(BitmapRegionDecoder decoder, Rect rect){
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = decoder.decodeRegion(rect,options);
return bitmap;
}
public static class BitmapCache{
private boolean isFixed;
private Bitmap candidate;
public BitmapCache(boolean isFixed, Bitmap candidate) {
this.isFixed = isFixed;
this.candidate = candidate;
}
public boolean isFixed() {
return isFixed;
}
public void setFixed(boolean fixed) {
isFixed = fixed;
}
public Bitmap getCandidate() {
return candidate;
}
public void setCandidate(Bitmap candidate) {
this.candidate = candidate;
}
}
}
3.缓存一般用LruCache(内存缓存)和DiskLruCache(磁盘缓存),它们都是采用Lru算法,即近期最少使用算法,缓存不足时会清掉最近最少使用的数据。
4.LruCache内部采用LinkedHashmap以强引用的方式存储对象,且LruCache是线程安全的,其get、set方法内部都用了synchronized关键字。LruCache对象要重写sizeOf方法,用来完成bitmap的大小计算,父类的sizeof方法默认返回1。存值用mLruCache.put(key,value)方法,取值用mLruCache.get(key)方法。
5.DiskLruCache的创建要用DiskLruCache.open(File directory,int appVersion,int valueCount,long maxSize)方法创建。directory表示存储路径,如果希望应用卸载的时候删除缓存,那么用SD卡的应用缓存目录,否则就用SD卡的其他目录;appVersion表示版本标识,发生改变时会清空缓存数据,一般来说不需要清就一直设为1;valueCount表示单个节点对应的数据个数,一般设为1;maxSize表示缓存的总大小。
6.DiskLruCache添加缓存要用Editor来实现,即DiskLruCache.Editor editor = diskLruCache.edit(key),以图片为例,key代表图片的url对应的key,之所以要转成key,是因为图片url中可能有特殊字符,转换规则如下:
public String getHashKeyFromUrl(String url){
String cacheKey;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(url.getBytes());
cacheKey = byteToHexString(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
cacheKey = String.valueOf(url.hashCode());
}
return cacheKey;
}
public String byteToHexString(byte[] bytes){
if(bytes==null){
return null;
}
StringBuilder builder = new StringBuilder();
int length = bytes.length;
for(int i=0 ; i<length ; i++){
String hex = Integer.toHexString(0xFF & bytes[i]);
if(hex.length()==1){
builder.append('0');
}
builder.append(hex);
}
return builder.toString();
}
通过Editor
得到文件输出流,再往里面写数据,最后通过Editor.commit
方法写入缓存。如下:
DiskLruCache.Editor editor = diskLruCache.edit(getHashKeyFromUrl(url));
OutputStream outputStream = editor.newOutputStream(0);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
ByteBuffer byteBuffer=ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(byteBuffer);
byte[] bytes=byteBuffer.array();
bufferedOutputStream.write(bytes);
editor.commit();
diskLruCache.flush();
7.DiskLruCache取缓存数据要用DiskLruCache.SnapShot,通过它可以拿到FileInputStream对象,然后通过mFileInputStream.getFD()方法拿到FileDescriptor,最后通过BitmapFactory.decodeFileDescriptor(FileDescriptor fd)方法拿到bitmap对象。如下:
Bitmap bitmap=null;
try {
DiskLruCache.Snapshot snapshot=diskLruCache.get(getHashKeyFromUrl(url));
FileInputStream fileInputStream= (FileInputStream) snapshot.getInputStream(0);
FileDescriptor fileDescriptor=fileInputStream.getFD();
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
} catch (IOException e) {
e.printStackTrace();
}
网友评论