当前做的项目中用到了离线头像图片,头像用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过于巨大,后面改成了文件地址。