IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Bitmap 研究与思考(中篇) -> 正文阅读

[移动开发]Android Bitmap 研究与思考(中篇)

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/123054991
本文出自【赵彦军的博客】

两年前写过一篇文章,Android Bitmap 研究与思考(上篇) 介绍了 bitmap 的基本概念,今天这篇文章就来看看 bitmap 的压缩问题

质量压缩

 private fun compressQuality() {
        //把 drawable 转成 bitmap 
        val bm = BitmapFactory.decodeResource(resources, R.drawable.a123)
        val bos = ByteArrayOutputStream()
        //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        bm.compress(Bitmap.CompressFormat.JPEG, 70, bos)
        val bytes = bos.toByteArray()
        //把数据流解码为 bitmap 
        val mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
    }

在解码的代码中,还可以这只解码参数 Options

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

设置采样率

val options = BitmapFactory.Options()
//设置采样率,为2的阶乘
options.inSampleSize = 16

我们去解析一个图片,如果太大,就会OOM,我们可以设置压缩比例inSampleSize,但是这个压缩比例设置多少就是个问题,所以我们解析图片可以分为俩个步骤,

  • 第一步就是获取图片的宽高,这里要设置Options.inJustDecodeBounds=true,这时候decode的bitmap为null,只是把图片的宽高放在Options里.

  • 第二步就是设置合适的压缩比例 inSampleSize ,这时候获得合适的Bitmap

测量解码完成后的bitmap宽高,解码返回值为null,bitmap 不会加载至内存。

val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
//decodeByteArray 返回值为 null
BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)

val realWidth = options.outWidth  //bitmap的宽度
val realHeight = options.outHeight //bitmap的高度

总结:

  • bitmap.compress 质量压缩方案,不会降低bitmap 内存占用。如果保存到磁盘会降低占用空间。
  • BitmapFactory.decodeByteArray 解码方案,使用采样率 inSampleSize 采样参数大于 1 会降低内存占用,保存到磁盘会降低磁盘占用空间。

采样率压缩

 private fun compressSampling() {
        //定义解码参数
        val options = BitmapFactory.Options()
        //设置采样率,为2的阶乘
        options.inSampleSize = 4
        val bitmap = BitmapFactory.decodeResource(resources, R.drawable.a123, options)
        binding.image2.setImageBitmap(bitmap)
    }

采样率压缩其原理其实也是缩放bitamp的尺寸,通过调节其inSampleSize参数,比如调节为2,宽高会为原来的1/2,内存变回原来的1/4.

小例子:当我们把 inSampleSize 设置为 256 的时候,看看什么效果。

在这里插入图片描述
可以看到当采样率很大的时候,图片会变模糊。

矩阵缩放

    private fun compressMatrix() {
        val options = BitmapFactory.Options()
        options.inSampleSize = 2
        val bitmap = BitmapFactory.decodeResource(resources, R.drawable.a123, options)

        val matrix = Matrix()
        matrix.setScale(0.25f, 0.25f)  //缩放
        matrix.setRotate(30f) //旋转
        val srcBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
    }

放缩法压缩使用的是通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率的原理一样。

RGB_565压缩

private void compressRGB565() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);
 }

这是通过压缩像素占用的内存来达到压缩的效果,一般不建议使用ARGB_4444,因为画质实在是辣鸡,如果对透明度没有要求,建议可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。

createScaledBitmap 压缩

private void compressScaleBitmap() {
        Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test);
        mSrcBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true);
        bm = null;
 }

将图片的大小压缩成用户的期望大小,来减少占用内存。

BitmapFactory.Options 属性介绍

在这里插入图片描述

bitmap 保存为文件

   /**
     * 保存bitmap到本地
     */
    private fun saveBitmap(bitmap: Bitmap, file: File) {
        FileOutputStream(file).use {
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
            it.flush()
        }
    }
    /**
     * 保存bitmap到本地
     */
    private fun saveBitmap(bitmap: Bitmap, file: File) {
        val bos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
        FileOutputStream(file).use {
            it.write(bos.toByteArray())
        }
    }

文件转为 bitmap

  /**
     * 保存bitmap到本地
     */
    private fun fileToBitmap(file: File): Bitmap {
        FileInputStream(file).use {
            return BitmapFactory.decodeStream(it)
        }
    }
   /**
     * 保存bitmap到本地
     */
    private fun fileToBitmap(file: File): Bitmap {
        FileInputStream(file).use {
            val data = it.readBytes()
            val option = BitmapFactory.Options()
            option.inSampleSize = 2
            return BitmapFactory.decodeByteArray(data, 0, data.size, option)
        }
    }
   /**
     * 保存bitmap到本地
     */
    private fun fileToBitmap(file: File): Bitmap {
        val option = BitmapFactory.Options()
        option.inSampleSize = 2
        return BitmapFactory.decodeFile(file.absolutePath, option)
    }

高效加载大位图

大位图加载时的OOM问题,解决方式是通过 inSample 属性创建一个原位图的子采样版本以减低内存。那么这里的采样率 inSample 值如何选取最好呢?

这里我们利用官方推荐的采样率最佳计算方式:基本步骤就是:
①获取位图原尺寸
②获取ImageView即最终图片显示的尺寸
③依据两种尺寸计算采样率(或缩放比例)。

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // 位图的原宽高通过options对象获取
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;
     //当要显示的目标大小和图像的实际大小比较接近时,会产生没必要的采样,先除以2再判断以防止过度采样
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

依据上面的最佳采样率计算方法,进一步可以封装出利用最佳采样率创建子采样版本再创建位图对象的方法,这里以从项目图片资源文件加载Bitmap对象为例:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
   //因为inJustDecodeBounds为true,所以不会创建Bitmap对象只会扫描轮廓从而给options对象的宽高属性赋值
    BitmapFactory.decodeResource(res, resId, options);

    // 计算最佳采样率
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 记得将inJustDecodeBounds属性设置回false值
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

bitmap 转为 drawable

val bitmap = BitmapFactory.decodeResource(resources, R.drawable.a123)
val drawable = BitmapDrawable(resources, bitmap)

drawable 转为 bitmap

    /**
     * drawable转为bitmap
     */
    private fun drawableToBitmap(drawable: Drawable): Bitmap {
        if (drawable is BitmapDrawable) {
            return drawable.bitmap
        }
        var bitmapWidth = drawable.intrinsicWidth
        var bitmapHeight = drawable.intrinsicHeight

        bitmapWidth = max(bitmapWidth, 1)  //最小为1
        bitmapHeight = max(bitmapHeight, 1)  //最小为1

        val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
        val canvas = Canvas()
        canvas.setBitmap(bitmap)
        drawable.setBounds(0, 0, bitmap.width, bitmap.height)
        drawable.draw(canvas)
        return bitmap
    }

调用:

val drawable = ResourcesCompat.getDrawable(resources, R.drawable.a123, null)
val bitmap = drawableToBitmap(drawable)

getResources().getDrawable() 过时的解决方法

在这里插入图片描述
public Drawable getDrawable(@DrawableRes int id) 已经过时,推荐使用 public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) .

解决办法:

1、当你这个Drawable不受主题影响时
ResourcesCompat.getDrawable(getResources(), R.drawable.name, null);

2、当你这个Drawable受当前Activity主题的影响时
ContextCompat.getDrawable(getActivity(), R.drawable.name);

3、当你这个Drawable想使用另外一个主题样式时
ResourcesCompat.getDrawable(getResources(), R.drawable.name, anotherTheme);

view 获取bitmap 对象

    private fun getBitmap(view: View): Bitmap {
        val width = view.width
        val height = view.height
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas()
        canvas.setBitmap(bitmap)
        view.draw(canvas)
        return bitmap
    }

ScrollView 获取 bitmap

    private fun getBitmap(scrollVew: ScrollView): Bitmap {
        val width = scrollVew.width
        var height = 0
        scrollVew.forEach {
            height += it.height
        }

        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas()
        canvas.setBitmap(bitmap)
        scrollVew.draw(canvas)
        return bitmap
    }

总结

以上5种就是我们常用的压缩方法了,这里的压缩也只是针对在运行加载的bitmap占用内存的大小。我们在做App内存优化的时候,一般可以从这两个方面入手,一个内存泄漏,另外一个是Bitmap压缩了,在要求像素不高的情况下,可以对Bitmap进行压缩,并且针对一些只使用一次的bitmap,要做好recycle的处理。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:41:20  更:2022-02-28 15:42:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 15:48:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码