Android高效开发(一)—合理使用图片资源

1,428 阅读5分钟

前言

在Android开发中,我们难免会遇到加载图片的场景。不管是网络图片、资源图片或者是SD卡中的图片,都需要加载到内存中使用。如果不能合理处理图片,可能会导致OOM的问题或者其他性能问题,接下就讨论下Android中图片资源处理的几种常用的方式。

Resource中的图片资源的处理

在Android中开发的时候都会用到很多图片素材,对于某些背景图片、各种图标或者其他一些提示图片我们通常会放到res目录中的drawable中。

我们知道在res目录下有很多drawable目录,比如:drawable、drawable-hdpi、drawable-mdpi等。这些目录对应了不同密度的屏幕,在不同密度的屏幕下会优先从对应目录下取图片资源。

目录名称 密度(Density)
res/drawable 0
res/drawable-ldpi 120
res/drawable-mdpi 160
res/drawable-hdpi 240
res/drawable-xhdpi 320
res/drawable-xxdpi 480

这六种目录代表了目前市面上Android机型不同的6中密度的屏幕。

注意:res/drawable目录的密度为0,不是说有机型的屏幕密度为0。这是默认密度,在不同机型下默认密度不一样。

Android如何找到res中图片资源

Android在加载资源图片时,会先获取当前设备的屏幕密度,然后根据屏幕密度去找图片资源。如果没有在对应的文件夹下找到图片资源,就会采取就近原则,从与屏幕密度最相近的图片资源目录获取。如果都没有就只能从默认的资源目录下获取(也就是res/drawable)。

在获取到图片资源时,如果密度不匹配,Android会对图片进行处理。比如在res/drawable-mdpi找到了图片,但是设备屏幕密度是xxdpi的,那么Android就会按照3倍大小缩放这张图片。根据图片内存的计算公式(图片高度 x 图片宽度 x 一个像素占用的内存大小)。比如:一张800x480的图片原来占用内存大约是1.46M,增加3倍后图片是3200x1920,占用内存大约是23M。而一般Android系统分配给每个进程的内存也才一百多M,在低配手机中很容易就产生OOM。

如何处理

针对上面的情况,如何处理资源文件呢?因为Android拿到低密度的图片时,在高密度设备会将其放大。所以在添加资源文件时,应该优先使用高品质图片从高密度的目录往下放。

Bitmap图片的处理

Bitmap类型的图片也是在开发中经常用到的。对于Bitmap图片如果不加处理直接使用的话,也很容易导致OOM。因为会有一些占用很大的图片被加载到内存中,而真正的需求可能并不需要这么大的图片。

接下来的内容大多来自于Google推荐的一种处理Bitmap图片的方法。链接在下面,感兴趣的同学也可以直接阅读文档。

developer.android.com/topic/perfo…

在Google的方案中,主要的思想是按需加载Bitmap。所谓的按需加载,就是根据图片的尺寸和要加载图片的ImageView的尺寸对Bitmap进行压缩处理。

获取Bitmap图片的尺寸

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

上面的代码会获取到Bitmap图片的宽、高、MimeType。同时这里有个参数是比较重要的,inJustDecodeBounds这个参数设为true的时候不会在内存中分配Bitmap的空间,只是获取到了Bitmap图片的宽高等信息。

计算压缩比例

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        //如果Bitmap图片宽高大于ImageView宽高,就缩放图片
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        //计算缩放比例
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

上面的代码通过图片的尺寸计算Bitmap图片的缩放比例。最终得到的结果inSampleSize就是需要缩放的比例。

获得图片

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

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

这一步是获取得到最终的Bitmap图片,可以看到BitmapFactory.Options options的inSampleSize是缩放比例,在获取图片之前,将inJustDecodeBounds设置为false,然后再次调用decode方法就获得了最终的Bitma对象。

使用图片加载框架

在处理图片时,特别是网络图片如果我们想要高效的利用图片资源。这个时候使用图片加载框架是一个很好的选择。比如:Glide框架。这也是Google推荐的图片加载框架。

图片加载框架不仅能帮助我们处理图片。同时还能通过缓存复用图片,提高图片的利用率。

总结

在Android开发中,图片的处理几乎是所以应用都要面对的场景,能够了解一些图片的优化的小技巧,能够提高我们在开发中的效率与质量。如果各位同学有更好的方法,欢迎在评论区讨论,大家共同学习。