内存大小的计算
ARGB_8888: A:表示透明度 R:表示红色 G:表示绿色 B:表示蓝色 每个占8bit 共占4byte
RGB_565占2byte
内存大小占用:
RGB_565 = width * height * 2 ARGB_8888 = width * height * 4
同一张图片 放到不同的dpi文件夹下加载到内存里面的占用大小是不一样的
对于一个adapter加载图片对比:
原始加载:
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_mv_w);
对于内存查看 是17.8M
图片压缩优化之后:
对于内存查看 是5.4M
Bitmap bitmap = ImageResize.resizeBitmap(context, R.drawable.icon_mv, 80, 80, false);
public class ImageResize {
/**
* 返回压缩图片
*
* @param context
* @param id
* @param maxW
* @param maxH
* @param hasAlpha
* @return
*/
public static Bitmap resizeBitmap(Context context, int id, int maxW, int maxH, boolean hasAlpha, Bitmap reusable) {
Resources resources = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
// 设置为true后,再去解析,就只解析 out 参数
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, id, options);
int w = options.outWidth;
int h = options.outHeight;
options.inSampleSize = calcuteInSampleSize(w, h, maxW, maxH);
if (!hasAlpha) {
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds = false;
// 复用, inMutable 为true 表示易变
options.inMutable = true;
options.inBitmap = reusable;
return BitmapFactory.decodeResource(resources, id, options);
}
/**
* 计算 缩放系数
*
* @param w
* @param h
* @param maxW
* @param maxH
* @return
*/
private static int calcuteInSampleSize(int w, int h, int maxW, int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH) {
inSampleSize = 2;
while (w / inSampleSize > maxW && h / inSampleSize > maxH) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
缓存优化之后:
Bitmap bitmap = ImageCache.getInstance().getBitmapFromMemory(String.valueOf(i)); Log.e("leo", "使用内存缓存" + bitmap); if (bitmap == null) { Bitmap reusable = ImageCache.getInstance().getReusable(60, 60, 1); Log.e("leo", "使用复用缓存" + reusable);
bitmap = ImageCache.getInstance().getBitmapFromDisk(String.valueOf(i), reusable);
Log.e("leo", "使用磁盘缓存" + reusable);
if (bitmap == null) {
// 网络获取
bitmap = ImageResize.resizeBitmap(context, R.drawable.icon_mv, 80, 80, false, reusable);
//放入内存
ImageCache.getInstance().putBitmap2Memory(String.valueOf(i), bitmap);
//放入磁盘
ImageCache.getInstance().putBitmap2Disk(String.valueOf(i), bitmap);
}
}
public class ImageCache {
private static ImageCache instance;
/**
* 单例类
*
* @return
*/
public static ImageCache getInstance() {
if (instance == null) {
synchronized (ImageCache.class) {
if (instance == null) {
instance = new ImageCache();
}
}
}
return instance;
}
private LruCache<String, Bitmap> lruCache;
private Set<WeakReference<Bitmap>> reusablePool;
private DiskLruCache diskLruCache;
public void init(Context context, String dir) {
// 复用池
reusablePool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
// 内存大小
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
//memoryClass * 1024 * 1024 / 8
lruCache = new LruCache<String, Bitmap>(memoryClass * 1024 * 1024 / 8) {
// 返回的一张图片大小
@Override
protected int sizeOf(String key, Bitmap value) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
return value.getAllocationByteCount();
}
return value.getByteCount();
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue.isMutable()) {
// 3.0 bitmap 缓存 native
// <8.0 bitmap 缓存 java
// 8.0 native
reusablePool.add(new WeakReference<Bitmap>(oldValue, getReferenceQueue()));
}
oldValue.recycle();
}
};
try {
diskLruCache = DiskLruCache.open(new File(dir), BuildConfig.VERSION_CODE, 1, 10 * 1024 * 1024);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 放入磁盘缓存
*/
public void putBitmap2Disk(String key, Bitmap bitmap) {
DiskLruCache.Snapshot snapshot = null;
OutputStream os = null;
try {
snapshot = diskLruCache.get(key);
if (snapshot == null) {
DiskLruCache.Editor edit = diskLruCache.edit(key);
if (edit != null) {
os = edit.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
edit.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (snapshot != null) {
snapshot.close();
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 从磁盘缓存获取bitmap
*
* @param key
* @param reusable
* @return
*/
public Bitmap getBitmapFromDisk(String key, Bitmap reusable) {
DiskLruCache.Snapshot snapshot = null;
Bitmap bitmap = null;
try {
snapshot = diskLruCache.get(key);
if (snapshot == null) {
return null;
}
InputStream is = snapshot.getInputStream(0);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
options.inBitmap = reusable;
bitmap = BitmapFactory.decodeStream(is, null, options);
if (bitmap != null) {
lruCache.put(key, bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (snapshot != null) {
snapshot.close();
}
}
return bitmap;
}
private ReferenceQueue<Bitmap> referenceQueue;
boolean shutDown;
private ReferenceQueue<Bitmap> getReferenceQueue() {
if (referenceQueue == null) {
referenceQueue = new ReferenceQueue<>();
new Thread(new Runnable() {
@Override
public void run() {
while (!shutDown) {
try {
Reference<? extends Bitmap> remove = referenceQueue.remove();
Bitmap bitmap = remove.get();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
return referenceQueue;
}
/**
* 把bitmap 放入内存
*
* @param key
* @param bitmap
*/
public void putBitmap2Memory(String key, Bitmap bitmap) {
lruCache.put(key, bitmap);
}
/**
* 从内存中取出bitmap
*
* @param key
* @return
*/
public Bitmap getBitmapFromMemory(String key) {
return lruCache.get(key);
}
public void clearMemory() {
lruCache.evictAll();
}
/**
* 3.0 之前不能复用
* 3.0-4.4 宽高一样,inSampleSize = 1
* 4.4 只要小于等于就行了
*
* @param w
* @param h
* @param inSampleSize
* @return
*/
public Bitmap getReusable(int w, int h, int inSampleSize) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return null;
}
Bitmap reusable = null;
Iterator<WeakReference<Bitmap>> iterator = reusablePool.iterator();
while (iterator.hasNext()) {
Bitmap bitmap = iterator.next().get();
if (bitmap != null) {
if (checkInBitmap(bitmap, w, h, inSampleSize)) {
reusable = bitmap;
iterator.remove();
break;
}
} else {
iterator.remove();
}
}
return reusable;
}
/**
* 校验bitmap 是否满足条件
*
* @param bitmap
* @param w
* @param h
* @param inSampleSize
* @return
*/
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return bitmap.getWidth() == w && bitmap.getHeight() == h && inSampleSize == 1;
}
if (inSampleSize > 1) {
w /= inSampleSize;
h /= inSampleSize;
}
int byteCount = w * h * getBytesPerPixel(bitmap.getConfig());
// 图片内存 系统分配内存
return byteCount <= bitmap.getAllocationByteCount();
}
/**
* 通过像素格式计算每一个像素占用多少字节
*
* @param config
* @return
*/
private int getBytesPerPixel(Bitmap.Config config) {
if (config == Bitmap.Config.ARGB_8888) {
return 4;
}
return 2;
}
}
加载长图
BigImageView bigImageView = findViewById(R.id.iv_big);
InputStream is = null;
try {
is = getAssets().open("big.png");
bigImageView.setImage(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import java.io.IOException;
import java.io.InputStream;
public class BigImageView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {
Rect mRect;
BitmapFactory.Options mOptions;
int mImageWidth;
int mImageHeight;
BitmapRegionDecoder mBitmapRegionDecoder;
private GestureDetector mGestureDetector;
private Scroller mScroller;
public BigImageView(Context context) {
this(context, null, 0);
}
public BigImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mRect = new Rect();
mOptions = new BitmapFactory.Options();
//手势
mGestureDetector = new GestureDetector(context, this);
// 将触摸事件交给手势处理
setOnTouchListener(this);
// 滑动帮助
mScroller = new Scroller(context);
}
public void setImage(InputStream is) {
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, mOptions);
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
mOptions.inMutable = true;
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
try {
// false 不共享 图片源
mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
int mViewHeight;
int mViewWidth;
float mScale;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewHeight = getMeasuredHeight();
mViewWidth = getMeasuredWidth();
if (mBitmapRegionDecoder == null) {
return;
}
mRect.left = 0;
mRect.top = 0;
mRect.right = mImageWidth;
// 缩放因子
mScale = mViewWidth / (float) mImageWidth;
// x * mscale = mViewHeight
mRect.bottom = (int) (mViewHeight / mScale);
// 第一种方式优化
// mOptions.inSampleSize = calcuteInSampleSize(mImageWidth, mImageHeight, mViewWidth, mViewHeight);
// 第二种方式优化
// float temp = 1.0f / mScale;
// if (temp > 1) {
// mOptions.inSampleSize = (int) Math.pow(2, (int) (temp));
// } else {
// mOptions.inSampleSize = 1;
// }
Log.e("Leo", "============缩放后=========");
Log.e("Leo", "inSampleSize = " + mOptions.inSampleSize);
Log.e("Leo", "mScale = " + mScale);
Log.e("Leo", "图片宽 = " + mImageWidth + ",高 = " + mImageHeight);
Log.e("Leo", "view 宽 = " + mViewWidth + ",高 = " + mViewHeight);
}
/**
* @param w 图片宽
* @param h 图片高
* @param maxW View 宽
* @param maxH View 高
* @return
*/
private static int calcuteInSampleSize(int w, int h, int maxW, int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH) {
inSampleSize = 2;
while (w / inSampleSize > maxW && h / inSampleSize > maxH) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Bitmap bitmap = null;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmapRegionDecoder == null) {
return;
}
mOptions.inBitmap = bitmap;
bitmap = mBitmapRegionDecoder.decodeRegion(mRect, mOptions);
Log.e("leo", "图片大小 " + bitmap.getByteCount());// 没有优化:44338752,1.优化:2770200,2优化:692064
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
// matrix.setScale(mScale * mOptions.inSampleSize, mScale * mOptions.inSampleSize);
canvas.drawBitmap(bitmap, matrix, null);
}
@Override
public boolean onDown(MotionEvent e) {
// 如果滑动还没有停止 强制停止
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
//继续接收后续事件
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//改变加载图片的区域
mRect.offset(0, (int) distanceY);
//bottom大于图片高了, 或者 top小于0了
if (mRect.bottom > mImageHeight) {
mRect.bottom = mImageHeight;
mRect.top = mImageHeight - (int) (mViewHeight / mScale);
}
if (mRect.top < 0) {
mRect.top = 0;
mRect.bottom = (int) (mViewHeight / mScale);
}
// 重绘
invalidate();
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
/**
* startX: 滑动开始的x坐标
* velocityX: 以每秒像素为单位测量的初始速度
* minX: x方向滚动的最小值
* maxX: x方向滚动的最大值
*/
mScroller.fling(0, mRect.top, 0, (int) -velocityY, 0, 0,
0, mImageHeight - (int) (mViewHeight / mScale));
return false;
}
/**
* 获取计算结果并且重绘
*/
@Override
public void computeScroll() {
//已经计算结束 return
if (mScroller.isFinished()) {
return;
}
//true 表示当前动画未结束
if (mScroller.computeScrollOffset()) {
mRect.top = mScroller.getCurrY();
mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
invalidate();
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// 事件交给手势处理
return mGestureDetector.onTouchEvent(event);
}
}