内存优化(二):内存大户,Bitmap内存优化

  • public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)

  • public static Bitmap decodeResource(Resources res, int id)  //从资源文件读取图片

  • public static Bitmap decodeResource(Resources res, int id, Options opts)

  • public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //从数组读取图片

  • public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)

  • public static Bitmap decodeFileDescriptor(FileDescriptor fd) //从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高

  • public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

** Bitmap.Config inPreferredConfig 😗*

枚举变量 (位图位数越高代表其可以存储的颜色信息越多,图像越逼真,占用内存越大)

  • public static final Bitmap.Config ALPHA_8  //代表8位Alpha位图 每个像素占用1byte内存

  • public static final Bitmap.Config ARGB_4444  //代表16位ARGB位图 每个像素占用2byte内存

  • public static final Bitmap.Config ARGB_8888  //代表32位ARGB位图 每个像素占用4byte内存

  • public static final Bitmap.Config RGB_565  //代表8位RGB位图 每个像素占用2byte内存

Android中一张图片(BitMap)占用的内存主要和以下几个因数有关:图片长度,图片宽度,单位像素占用的字节数。一张图片(BitMap)占用的内存=图片长度_图片宽度_单位像素占用的字节数。


三、Bitmap加载方式


Bitmap的加载方式有Resource资源加载、本地(SDcard)加载、网络加载等加载方式。

1. 从本地(SDcard)文件读取
  • 方式一

/**

  • 获取缩放后的本地图片

  • @param filePath 文件路径

  • @param width 宽

  • @param height 高

  • @return

*/

public static Bitmap readBitmapFromFile(String filePath, int width, int height) {

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeFile(filePath, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeFile(filePath, options);

}

  • 方式二 (效率高于方式一)

/**

  • 获取缩放后的本地图片

  • @param filePath 文件路径

  • @param width 宽

  • @param height 高

  • @return

*/

public static Bitmap readBitmapFromFileDescriptor(String filePath, int width, int height) {

try {

FileInputStream fis = new FileInputStream(filePath);

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);

} catch (Exception ex) {

}

return null;

}

2. 从输入流中读取文件(网络加载)

/**

  • 获取缩放后的本地图片

  • @param ins 输入流

  • @param width 宽

  • @param height 高

  • @return

*/

public static Bitmap readBitmapFromInputStream(InputStream ins, int width, int height) {

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeStream(ins, null, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeStream(ins, null, options);

}

3.Resource资源加载
  1. Res资源加载方式:

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeResource(resources, resourcesId, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeResource(resources, resourcesId, options);

}

此种方式相当的耗费内存 建议采用decodeStream代替decodeResource 可以如下形式:

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {

InputStream ins = resources.openRawResource(resourcesId);

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeStream(ins, null, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeStream(ins, null, options);

}

BitmapFactory.decodeResource 加载的图片可能会经过缩放,该缩放目前是放在 java 层做的,效率比较低,而且需要消耗 java 层的内存。因此,如果大量使用该接口加载图片,容易导致OOM错误

BitmapFactory.decodeStream 不会对所加载的图片进行缩放,相比之下占用内存少,效率更高。

这两个接口各有用处,如果对性能要求较高,则应该使用 decodeStream;如果对性能要求不高,且需要 Android 自带的图片自适应缩放功能,则可以使用 decodeResource。

  1. Assets资源加载方式:

/**

  • 获取缩放后的本地图片

  • @param filePath 文件路径,即文件名称

  • @return

*/

public static Bitmap readBitmapFromAssetsFile(Context context, String filePath) {

Bitmap image = null;

AssetManager am = context.getResources().getAssets();

try {

InputStream is = am.open(filePath);

image = BitmapFactory.decodeStream(is);

is.close();

} catch (IOException e) {

e.printStackTrace();

}

return image;

}

4.从二进制数据读取图片

public static Bitmap readBitmapFromByteArray(byte[] data, int width, int height) {

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeByteArray(data, 0, data.length, options);

float srcWidth = options.outWidth;

float srcHeight = options.outHeight;

int inSampleSize = 1;

if (srcHeight > height || srcWidth > width) {

if (srcWidth > srcHeight) {

inSampleSize = Math.round(srcHeight / height);

} else {

inSampleSize = Math.round(srcWidth / width);

}

}

options.inJustDecodeBounds = false;

options.inSampleSize = inSampleSize;

return BitmapFactory.decodeByteArray(data, 0, data.length, options);

}


四、Bitmap | Drawable | InputStream | Byte[ ] 之间进行转换


  1. Drawable转化成Bitmap

public static Bitmap drawableToBitmap(Drawable drawable) {

Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);

Canvas canvas = new Canvas(bitmap);

drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

drawable.draw(canvas);

return bitmap;

}

drawable的获取方式:Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);

  1. Bitmap转换成Drawable

public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {

Drawable drawable = new BitmapDrawable(resources, bm);

return drawable;

}

  1. Bitmap转换成byte[]

public byte[] bitmap2Bytes(Bitmap bm) {

ByteArrayOutputStream baos = new ByteArrayOutputStream();

bm.compress(Bitmap.CompressFormat.PNG, 100, baos);

return baos.toByteArray();

}

  1. byte[]转换成Bitmap

Bitmap bitmap = BitmapFactory.decodeByteArray(byte, 0, b.length);

  1. InputStream转换成Bitmap

InputStream is = getResources().openRawResource(id);

Bitmap bitmap = BitmaoFactory.decodeStream(is);

  1. InputStream转换成byte[]

InputStream is = getResources().openRawResource(id);//也可以通过其他方式接收一个InputStream对象

ByteArrayOutputStream baos = new ByteArrayOutputStream();

byte[] b = new byte[1024*2];

int len = 0;

while ((len = is.read(b, 0, b.length)) != -1)

{

baos.write(b, 0, len);

baos.flush();

}

byte[] bytes = baos.toByteArray();

五、Bitmap简单操作


  1. 将Bitmap保存为本地文件:

public static void writeBitmapToFile(String filePath, Bitmap b, int quality) {

try {

File desFile = new File(filePath);

FileOutputStream fos = new FileOutputStream(desFile);

BufferedOutputStream bos = new BufferedOutputStream(fos);

b.compress(Bitmap.CompressFormat.JPEG, quality, bos);

bos.flush();

bos.close();

} catch (IOException e) {

e.printStackTrace();

}

}

  1. 图片压缩:

private static Bitmap compressImage(Bitmap image) {

if (image == null) {

return null;

}

ByteArrayOutputStream baos = null;

try {

baos = new ByteArrayOutputStream();

image.compress(Bitmap.CompressFormat.JPEG, 100, baos);

byte[] bytes = baos.toByteArray();

ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);

Bitmap bitmap = BitmapFactory.decodeStream(isBm);

return bitmap;

} catch (OutOfMemoryError e) {

} finally {

try {

if (baos != null) {

baos.close();

}

} catch (IOException e) {

}

}

return null;

}

  1. 图片缩放:

/**

  • 根据scale生成一张图片

  • @param bitmap

  • @param scale 等比缩放值

  • @return

*/

public static Bitmap bitmapScale(Bitmap bitmap, float scale) {

Matrix matrix = new Matrix();

matrix.postScale(scale, scale); // 长和宽放大缩小的比例

Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

return resizeBmp;

}

  1. 获取图片旋转角度:

/**

  • 读取照片exif信息中的旋转角度

  • @param path 照片路径

  • @return角度

*/

private static int readPictureDegree(String path) {

if (TextUtils.isEmpty(path)) {

return 0;

}

int degree = 0;

try {

ExifInterface exifInterface = new ExifInterface(path);

int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

switch (orientation) {

case ExifInterface.ORIENTATION_ROTATE_90:

degree = 90;

break;

case ExifInterface.ORIENTATION_ROTATE_180:

degree = 180;

break;

case ExifInterface.ORIENTATION_ROTATE_270:

degree = 270;

break;

}

} catch (Exception e) {

}

return degree;

}

  1. 设置图片旋转角度

private static Bitmap rotateBitmap(Bitmap b, float rotateDegree) {

if (b == null) {

return null;

}

Matrix matrix = new Matrix();

matrix.postRotate(rotateDegree);

Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);

return rotaBitmap;

}

  1. 通过图片id获得Bitmap:

Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

  1. 通过 assest 获取 获得Drawable bitmap:

InputStream in = this.getAssets().open(“ic_launcher”);

Drawable da = Drawable.createFromStream(in, null);

Bitmap mm = BitmapFactory.decodeStream(in);

  1. 通过 sdcard 获得 bitmap

Bitmap bit = BitmapFactory.decodeFile("/sdcard/android.jpg");

  1. view转Bitmap

public static Bitmap convertViewToBitmap(View view, int bitmapWidth, int bitmapHeight){

Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);

view.draw(new Canvas(bitmap));

return bitmap;

}

  1. 将控件转换为bitmap

public static Bitmap convertViewToBitMap(View view){

// 打开图像缓存

view.setDrawi
ngCacheEnabled(true);

// 必须调用measure和layout方法才能成功保存可视组件的截图到png图像文件

// 测量View大小

view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

// 发送位置和尺寸到View及其所有的子View

view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

// 获得可视组件的截图

Bitmap bitmap = view.getDrawingCache();

return bitmap;

}

public static Bitmap getBitmapFromView(View view){

Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(returnedBitmap);

Drawable bgDrawable = view.getBackground();

if (bgDrawable != null)

bgDrawable.draw(canvas);

else

canvas.drawColor(Color.WHITE);

view.draw(canvas);

return returnedBitmap;

}

  1. 放大缩小图片

public static Bitmap zoomBitmap(Bitmap bitmap,int w,int h){

int width = bitmap.getWidth();

int height = bitmap.getHeight();

Matrix matrix = new Matrix();

float scaleWidht = ((float)w / width);

float scaleHeight = ((float)h / height);

matrix.postScale(scaleWidht, scaleHeight);

Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

return newbmp;

}

  1. 获得圆角图片的方法

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){

Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap

.getHeight(), Config.ARGB_8888);

Canvas canvas = new Canvas(output);

final int color = 0xff424242;

final Paint paint = new Paint();

final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

final RectF rectF = new RectF(rect);

paint.setAntiAlias(true);

canvas.drawARGB(0, 0, 0, 0);

paint.setColor(color);

canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));

canvas.drawBitmap(bitmap, rect, rect, paint);

return output;

}

  1. **对 bitmap 进行裁剪 **

public Bitmap bitmapClip(Context context , int id , int x , int y){

Bitmap map = BitmapFactory.decodeResource(context.getResources(), id);

map = Bitmap.createBitmap(map, x, y, 120, 120);

return map;

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取
F(rect);

paint.setAntiAlias(true);

canvas.drawARGB(0, 0, 0, 0);

paint.setColor(color);

canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));

canvas.drawBitmap(bitmap, rect, rect, paint);

return output;

}

  1. **对 bitmap 进行裁剪 **

public Bitmap bitmapClip(Context context , int id , int x , int y){

Bitmap map = BitmapFactory.decodeResource(context.getResources(), id);

map = Bitmap.createBitmap(map, x, y, 120, 120);

return map;

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-TTwMkBuE-1719339671266)]

[外链图片转存中…(img-amBxWSCw-1719339671267)]

[外链图片转存中…(img-4AVe4oIk-1719339671268)]

[外链图片转存中…(img-69DGHDvl-1719339671269)]

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果你需要这些资料, ⬅ 专栏获取

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值