优化base64图片列表的oom问题

1,201 阅读2分钟

  当前做的项目中用到了离线头像图片,头像用base64图片的方式提前获取,加载的时候遇到了oom问题,经过一系列优化,解决了问题,核心思想是尽可能减少对base64和bitmap的处理,多数时候传递数据使用文件路径或者key。

  第一步,让接口人员把头像数据压缩,在看待base64图片时,我们不能把它当作string看待,直接视为图片看待,图片数据过大会直接导致base64字符串直接占用过多内存导致oom。

  第二步,获取到base64数据以后解码成byte[]保存到本地文件,直接用图片的形式保存。

/** * 保存头像数据到本地 * * @param base64 */
public static String saveAvatar(String name, String base64) {
    if (TextUtils.isEmpty(base64))
        return "";
    File file = getAvatarFile(name);
    try {
        byte[] decode = Base64.decode(base64, Base64.DEFAULT);
        FileUtil.writeByteArrayToFile(file, decode);
    } catch (IOException e) {
        LegoLog.e(e);//打印错误日志
    }
    return file.getAbsolutePath();
}

  返回文件的路径,在常规的android开发中,往往使用okhttp之类的网络加载框架,这个时候接口数据和本地bean往往属性名称一一对应,这个时候,可以将之前对应的base64数据的字段置null,然后再保存在本地数据库中,减少数据传输中的内存占用。

  第三步,头像加载,为了减少加载时间和内存,可以使用Lruchache。

public static LruCache<String, Bitmap> cache;
public void initCache() {
    if (cache != null)
        return;
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    int size = maxMemory / 8;
    cache = new LruCache<String, Bitmap>(size) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return (value.getRowBytes()*value.getHeight() )/ 1024;
        }
    };}
public void addToCache(String key, Bitmap value) {
    if (null == getBitmapFromCache(key)) {
        cache.put(key, value);
    }
}
public Bitmap getBitmapFromCache(String key) {
    if (null != cache)
        return cache.get(key);
    return null;
}

  加载图片

Bitmap bitmap = ImageUtils.getInstance().getBitmapFromCache(path);
if (null == bitmap) {
    bitmap = MyFileUtils.getAvatarBitmap(path);
    if (null != bitmap)
        ImageUtils.getInstance().addToCache(path, bitmap);
}
if (null == bitmap) {
    imageView.post(() -> {
        imageView.setImageResource(R.mipmap.ic_avatar);
    });
    return;
}
Bitmap finalBitmap = bitmap;imageView.post(new Runnable() {
    @Override
    public void run() {
        imageView.setImageBitmap(finalBitmap);
    }
});

/** 
* 根据文件地址获取到bitmap 
*
* @param path * @return 
*/
public static Bitmap getAvatarBitmap(String path) {
    File file = new File(path);
    if (!file.exists())
        return null;
    Bitmap bitmap = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        byte[] bytes = FileUtil.readFileToByteArray(file);
        bitmap = BitmapUtil.byteToBitmap(bytes);
    } catch (IOException e) {
        LegoLog.e(e);
    } finally {
        try {
            baos.close();
        } catch (IOException e) {
            LegoLog.e(e);
        }
    }
    return bitmap;
}

  PS. 有两个方法是公司框架实现的,(writeByteArrayToFile/readFileToByteArray)就不具体写了。

  在列表中加载图片的时候,加载图片因为是耗时操作,写在子线程,记得使用线程池,否则可能线程开多了也会导致oom。

  我这里没有做常规的bitmap压缩操作(用Options压缩),为的还是减少bitmap操作,将图片压缩放到服务器来做,本地操作的bitmap越少,oom的几率也越少。

  在开发中曾经遇到过一个坑,就是直接用base64作为lrucache的key,这就是犯了一开始说的错误,没有把base64作为文件看待,key过于巨大,后面改成了文件地址。