【问题标题】:Android Bitmap Compression insufficient and causing OutOfMemoryError crashesAndroid Bitmap Compression 不足导致 OutOfMemoryError 崩溃
【发布时间】:2015-12-22 21:07:31
【问题描述】:

我基本上是在尝试压缩和传递用户选择的图像的 Base64 表示,但是应用程序在不同的手机上崩溃并出现 OutOfMemoryError 问题。这是我的压缩和转换代码:

Bitmap bm = BitmapFactory.decodeFile(filePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] byteArrayImage = baos.toByteArray();
String base64String = Base64.encodeToString(byteArrayImage, Base64.DEFAULT);

这个过程也非常缓慢,有时会导致应用崩溃。

这是我得到的一个例外:

    java.lang.OutOfMemoryError: Failed to allocate a 5035548 byte allocation with 5011320 free bytes and 4MB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:625)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:460)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:973)
    at android.content.res.Resources.loadDrawableForCookie(Resources.java:2477)

我应该做哪些改变?

【问题讨论】:

    标签: java android performance android-activity bitmap


    【解决方案1】:
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inSampleSize = 2;  //you can also calculate your inSampleSize
    options.inJustDecodeBounds = false;
    options.inTempStorage = new byte[16 * 1024];
    
    Bitmap bm = BitmapFactory.decodeFile(filePath,options); //changed line code
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] byteArrayImage = baos.toByteArray();
    String base64String = Base64.encodeToString(byteArrayImage, Base64.DEFAULT);
    

    注意:android:largeHeap="true" 用于您的应用程序并不被认为是理想的解决方案。

    这是来自谷歌的解释,

    但是,请求大堆的能力仅适用于 可以证明需要消耗更多 RAM 的一小部分应用程序(例如 作为大型照片编辑应用程序)。永远不要简单地请求大堆 因为你的内存已经用完了,你需要一个快速修复——你应该 仅当您确切知道所有内存在哪里时才使用它 分配以及为什么必须保留它。然而,即使你很自信 您的应用程序可以证明大堆是合理的,您应该避免要求它 任何可能的程度。使用额外的内存将越来越多 不利于整体用户体验,因为垃圾 收集将花费更长的时间并且系统性能可能会变慢 任务切换或执行其他常见操作。

    这是文档https://developer.android.com/training/articles/memory.html的完整链接

    编辑 1: 要像 WhatsApp 图像压缩 等有效缩放图像,请查看此SO Answer

    【讨论】:

    • 感谢您的详细解释。使用此解决方案压缩后输出图像的质量如何?影响大吗?
    • 平均图像看起来不错,但图像越高清,需要的时间越长..我们可以加快速度吗?
    • 是的,您可以为此缩放图像。查看Edit 1
    • 如何将“选项”设置为已经存在的位图?没有解码我的意思是
    • Vipul 构造函数不存在:/
    【解决方案2】:

    尝试在使用后回收您的位图。并将位图也设置为空。如果你想运行垃圾收集器。

    【讨论】:

    • bm.recycle(); BM=空; System.gc();
    【解决方案3】:

    这种方法不适用于非常大的图片,比如从相机拍摄的图片。一张 13 mp 的照片是 4128x3096x3 字节,大约是 40 兆字节。那是位图的大小。如果您正在动态创建 base-64 表示,则需要另外 40 MB 甚至更多,因为 base-64 字符串的存储字节数比可比较的原始字节数组(位图)要多。

    你真的需要把它变成base 64吗?例如,如果你想上传它,你可以直接通过 rest api 或 multipart post request 来完成。

    如果你不能这样做,也许你可以拆分操作,比如每 1 MB,或者不是将该字符串写入内存,而是可以将其写入文件并在每 1 MB 操作后追加它?

    【讨论】:

    • 您认为哪种解决方案更好?分段上传还是 Base64? facebook、instagram等都遵循什么方法?
    • @Earthling Multipart 是上传图像的更好解决方案,因为使用 base64 将图像转换为字符串会将其大小增加 33%,从而占用更多带宽。跨度>
    • @VipulAsri 你有我可以查看的资源吗?我需要通过 Rest Service Post 上传它
    • @Earthling 这个实现正在使用 Volley:gist.github.com/vipulasri/b96488c612c1f150a1da
    猜你喜欢
    • 2018-02-07
    • 1970-01-01
    • 2021-11-30
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多