3分钟教你图解Bitmap编码传输

在Android的图片传输的流程为:

Created with Raphaël 2.1.2 Bitmap 二进制流 Byte数组 Base64编码 String进行传输 Base64解码 还原为原来的Byte数组,然后转换为原来的String

Bitmap的转换为二进制流:

//将bitmap转换为二进制流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

其中,我们知道compress的方法是进行压缩的,其中方法里面的三个参数,分别代表:图片格式、压缩比例、字节流(节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中)。

其实这个时候我想到了一个问题,当压缩比例为100的时候,也就是没有进行压缩,那这一步的代码意义何在呢? 经过研究下面源码:

 /**
     * Write a compressed version of the bitmap to the specified outputstream.
     * If this returns true, the bitmap can be reconstructed by passing a
     * corresponding inputstream to BitmapFactory.decodeStream(). Note: not
     * all Formats support all bitmap configs directly, so it is possible that
     * the returned bitmap from BitmapFactory could be in a different bitdepth,
     * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
     * pixels).
     *
     * @param format   The format of the compressed image
     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
     *                 small size, 100 meaning compress for max quality. Some
     *                 formats, like PNG which is lossless, will ignore the
     *                 quality setting
     * @param stream   The outputstream to write the compressed data.
     * @return true if successfully compressed to the specified stream.
     */
    public boolean compress(CompressFormat format, int quality, OutputStream stream) {
        checkRecycled("Can't compress a recycled bitmap");
        // do explicit check before calling the native method
        if (stream == null) {
            throw new NullPointerException();
        }
        if (quality < 0 || quality > 100) {
            throw new IllegalArgumentException("quality must be 0..100");
        }
        StrictMode.noteSlowCall("Compression of a bitmap is slow");
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
        boolean result = nativeCompress(mNativePtr, format.nativeInt,
                quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        return result;
    }

其中最重要的一句代码是这个:

nativeCompress(mNativePtr, format.nativeInt,
                quality, stream, new byte[WORKING_COMPRESS_STORAGE]);

再深入发现调用的是这个方法:

private static native boolean nativeCompress(long nativeBitmap, int format,
                                            int quality, OutputStream stream,
                                            byte[] tempStorage);

我们最后发现它最后是调用到了JNI,直接使用.cpp方法里面的C++函数了。这个大家有兴趣可以去参考这篇文章:Bitmap压缩原理解析与Android 7.0之前通过NDK使用libjpeg库高质量压缩图片。
所以回到我们的第一个源码那,看注释,第一句是 Write a compressed version of the bitmap to the specified outputstream. 意思就是将压缩后的Bitmap写入到指定的输出流里面。这样我就弄懂了,其实这个压缩如果是100的话,其最终的目的也就是去将bitmap写入到流里面。流(Stream)二进制数据流,内存中电信号转换,写入到磁盘的存储颗粒上,记录电信号。

二进制流转换为Byte[]数组

  • 这个就涉及到了编码的知识了。在最早期的时候,大家的编码方式都使用的是ASCII码的编码方式,而我们要知道的是在计算机中,所有的信息都是以0和1来进行存储的,指定的二进制代表了字母、标点符号、控制字符。譬如存储在计算机上的01000001代表了大写字母A。ASCII码一共有256个,但是常用的也就前128个,标准ASCII码(0~127)。
  • 简而言之,对于字符而言,直接用ASCII码对应的数字表示,ASCII码就是用来将字符存储到计算机的,例如“3” 的ASCII码的十进制为51 ,二进制为 0011 0011 。则是将二进制的值存储到了计算机。
  • 那么,由此延伸的一个问题是:当本身就是十进制的51,其二进制也为0011 0011 ,存储进了计算机。那么在计算机中的二进制 0011 0011,表现到内存中到底是字符还是数字呢? 我自我理解是计算机中既然存储了0011 0011 那么在内存中表示出来的值,只与其定义的数有关,如果你定义的是int 那么就是51;如果你定义的是String 那么就是“3”。
  • 而Byte里面存储为8bit,可以存储所有ASCII所有字符(这是它包含8bits的初衷)。也就是说1Byte里面有8bit的二进制,那么将二进制流每8bit存储于一个Byte,就可以得到最后转换的Byte[]数组了。

Byte[]数组转Base64编码

什么是Base64编码

漫画:什么是Base64算法?

为什么要进行Base64编码

因为Http协议(超文本传输协议)中,Byte里面的二进制可能为0000 0001B,如果直接转换为String的话,明显是不可能的,因为这个不是可打印字符,那么传输过程中可能就会出现乱码,而Base64也就是64个可打印的字符串。

Value Char Value Char Value Char Value Char
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

你可能感兴趣的:(算法)