Android部分-12:图片编程

1.你对Bitmap了解吗?它在内存中如何存在?

Bitmap位图包括像素以及长、宽、颜色等描述信息。长宽和像素位数是用来描述图片的,可以通过这些信息可以计算图片所占内存大小。位图可以理解为一个画架,把图放在上面然后可以对图片做一系列的处理。位图文件图像显示效果好,但是非压缩格式,需要占用较大的内存空间。

Config:表示图片像素类型,包括ALPHA_8、RGB565、ARGB_4444、ARGB_8888

  • ARGB_8888:4个通道都是8位,每个像素占用4个字节,图像效果最好,但占用内存较大
  • ARGB_4444:4个通道都是4位,每个像素占用2个字节,图像失真较为严重
  • RGB565:没有A通道,3个通道,每个像素占用2个字节,图像失真小,但是没有透明度。
  • ALPHA_8:只有A通道,每个像素占用1个字节,只有透明度,没有颜色值

压缩格式CompressFormat:CompressFormat.JPEG、CompressFormat.PNG和CompressFormat.WEBP

  • JPEG:一种有损压缩(JPEG2000即可以有损也可以无损),文件后缀 .jpg.jpeg;采用直接色,有丰富的色彩适合存储图片和生动效果图像;缺点:有损,不适合存储logo或线框图等等。

  • PNG:一种无损压缩,文件后缀.png;优点:支持透明,无损,主要用于小图标,透明背景等;缺点:若色彩复杂,则图片生成后文件很大。

  • WEBP:以WebP算法进行压缩;Google开发的新的图片格式,同时支持无损和有损压缩,使用直接色。无损压缩,相同质量的webp比PNG小大约26%;有损压缩,相同质量的webp比JPEG小25%-34% 支持动图,基本取代gif

2.有关Bitmap导致OOM的原因知道吗?如何优化?

  • 采样压缩

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    BitmapFactory.Options options = new BitmapFactory.Options();
    //inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), imgResId, options);
    //利用返回的原图片的宽高,我们就可以计算出缩放比inSampleSize(只能是2的整数次幂)
    options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight);
    options.inPreferredConfig = Bitmap.Config.RGB_565;

    //inJustDecodeBounds为false,返回bitmap
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),imgResId,options);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int halfWidth = width / 2;
    int halfHeight = height / 2;
    int inSampleSize = 1;
    if ((halfWidth / inSampleSize) >= reqWidth && (halfHeight / inSampleSize) >= reqHeight) {
    inSampleSize *= 2;
    }
    return inSampleSize;
    }
  • 缓存策略

    LruCache 和 DiskLruCache

  • 异步加载

    Handler 和 线程池

3.给我谈谈图片压缩。

  • 质量压缩

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * 质量压缩
    *
    * @param srcBitmap 源bitmap
    * @param compressFormat 图片压缩格式
    * @param quality 压缩质量
    * @return 返回压缩之后的bitmap
    */
    public static Bitmap compressByQuality(Bitmap srcBitmap, Bitmap.CompressFormat compressFormat, int quality) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    srcBitmap.compress(Bitmap.CompressFormat.WEBP, quality, byteArrayOutputStream);
    byte[] bytes = byteArrayOutputStream.toByteArray();
    return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    }
  • 采样压缩

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * 采样压缩
    *
    * @param context 上下文
    * @param resId 资源文件id
    * @param reqWidth 设置的图片宽度
    * @param reqHeight 设置的图片高度
    * @return 返回采样后的Bitmap
    */
    public static Bitmap compressBySampleSize(Context context, int resId, int reqWidth, int reqHeight) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    // 仅获取尺寸信息
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(context.getResources(), resId, options);
    options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight);
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(context.getResources(), resId, options);

    }

4.LruCache & DiskLruCache原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}

V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}

if (previous != null) {
entryRemoved(false, key, previous, value);
}

trimToSize(maxSize);
return previous;
}
1
2
3
4
5
6
7
8
//在LruCache构造方法中通过maxsize指定最大内存,通过重写sizeOf方法指定添加的数据占用内存大小
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}

5.说说你平常会使用的一些第三方图片加载库,最好给我谈谈它的原理。

6.如果让你设计一个图片加载库,你会如何设计?

缓存,压缩。

7.有一张非常大的图片,你如何去加载这张大图片?

BitmapRegionDecoder,按照区域进行加载。BitmapRegionDecoder.newInstance。

8.你知道Android中处理图片的一些库吗(OpenCv & GPUImage …)?

9.如何计算一张图片在内存中占用的大小?

查看详情

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×