Android端 WebP图片压缩与传输的一点探索

1. 简介 
直到4g时代,流量依然是宝贵的东西。而移动网络传输中,最占流量的一种载体:图片,成为了我们移动开发者不得不关注的一个问题。 
我们关注的问题,无非是图片体积和质量如何达到一个比较和谐的平衡,希望得到质量不错的图片同时体积还不能太大。 
走在时代前列的谷歌给出了一个不错的答案——WebP。 
WebP是一种图片文件格式,在相同的压缩指标下,webp的有损压缩能比jpg小 25-34%。而在我自己的测试里,有时候能小50%。

2. 大企业背书 
WebP在2010年发布第一个版本,到现在已经6年了,谷歌旗下的各种网站G+、以及非常有代表性的YouTube,他的视频文件格式WebM就是基于WebP构造的。 
据说腾讯、淘宝、美团也有部分应用。

3. Android 端 JPG 转换 WebP

RxJava线程转换:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    String[] imgs = new String[]{<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"1.jpg"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"2.jpg"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"3.jpg"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"4.jpg"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"5.jpg"</span>}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
    String path = Environment<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getExternalStorageDirectory</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getAbsolutePath</span>() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/Pictures/test/"</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.onCreate</span>(savedInstanceState)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        setContentView(R<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.layout</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.activity</span>_main)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
//      test = Api<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getBuilder</span>()<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.create</span>(Test<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.class</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        String[] permissions = {Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_EXTERNAL_STORAGE
                , Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.READ</span>_PHONE_STATE
                , Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.CAMERA</span>}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        if (Build<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.VERSION</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.SDK</span>_INT >= Build<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.VERSION</span>_CODES<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.M</span>) {
            requestPermissions(permissions, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        }
        compress()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
    }
    private void compress() {
        Observable<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.from</span>(imgs)
                <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.subscribeOn</span>(Schedulers<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.io</span>())
                <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.doOnNext</span>(new Action1<String>() {
                    @Override
                    public void <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">call</span>(String imgName) {
                        compress(imgName)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
                    }
                })
                <span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.subscribe</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
    }
    private void compress(String imgName) {
        try {
            File file = new File(path, imgName)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            Log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.i</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"jpg start"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            byte[] bytes = BitmapUtil<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.compressBitmapToBytes</span>(file<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getPath</span>(), <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">600</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">60</span>, Bitmap<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.CompressFormat</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.JPEG</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            File jpg = new File(path, imgName + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress.jpg"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            FileUtils<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.writeByteArrayToFile</span>(jpg, bytes)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            Log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.i</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"jpg finish"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            Log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.i</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"----------------------------------------------------"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            Log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.i</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"webp start"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            byte[] bytes1 = BitmapUtil<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.compressBitmapToBytes</span>(file<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.getPath</span>(), <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">600</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">60</span>, Bitmap<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.CompressFormat</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WEBP</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;//分别是图片路径,宽度高度,质量,和图片类型,重点在这里。</span>
            File webp = new File(path, imgName + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress.webp"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            FileUtils<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.writeByteArrayToFile</span>(webp, bytes1)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
            Log<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.i</span>(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"compress"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"webp finish"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        } catch (IOException e) {
            e<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.printStackTrace</span>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
        }
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li></ul>

我的测试机器也是Oneplus 1 ,CM13,所以需要获取相应的权限。 
利用RxJava来做线程操作,在io线程里做了耗时操作。

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] <span class="hljs-title" style="box-sizing: border-box;">compressBitmapToBytes</span>(String filePath, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqWidth, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqHeight, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> quality, Bitmap.CompressFormat format) {
        Bitmap bitmap = getSmallBitmap(filePath, reqWidth, reqHeight);
        ByteArrayOutputStream baos = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> ByteArrayOutputStream();
        bitmap.compress(format, quality, baos);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">byte</span>[] bytes = baos.toByteArray();
        bitmap.recycle();
        Log.i(TAG, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Bitmap compressed success, size: "</span> + bytes.length);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> bytes;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> Bitmap <span class="hljs-title" style="box-sizing: border-box;">getSmallBitmap</span>(String filePath, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqWidth, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqHeight) {
        BitmapFactory.Options options = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> BitmapFactory.Options();
        options.inJustDecodeBounds = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">true</span>;
        BitmapFactory.decodeFile(filePath, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>;
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//      options.inPreferQualityOverSpeed = true;</span>
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> BitmapFactory.decodeFile(filePath, options);
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">calculateInSampleSize</span>(BitmapFactory.Options options, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqWidth, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> reqHeight) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> h = options.outHeight;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> w = options.outWidth;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> inSampleSize = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (h > reqHeight || w > reqWidth) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> ratioW = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>) w / reqWidth;
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span> ratioH = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">float</span>) h / reqHeight;
            inSampleSize = (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) Math.min(ratioH, ratioW);
        }
        inSampleSize = Math.max(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, inSampleSize);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> inSampleSize;
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

根据输入的宽高值计算分辨率的缩小比例,再根据输入的压缩质量数值,压缩图片,获得压缩后bitmap,然后再将其保存成本地文件。 
这是非常常见的图片压缩手段。

4. WebP对比 
我用我日常生活里拍下的照片来做简单的对比测试,不是特别严谨,仅供简单的参考。 
拍照设备是刷了CM13的 一加 1 。拍照场景都是日常生活特别常见的。

以下是原图预览,就不一个个放出来了,太大。

这里写图片描述

缩小分辨率,同时压缩质量

文件名 照片原图 压缩后jpg 压缩后webp 压缩比
1.jpg 5760 kb 98 kb 74 kb 24.49%
2.jpg 4534 kb 64 kb 35 kb 45.31%
3.jpg 4751 kb 93 kb 68 kb 26.88%
4.jpg 7002 kb 121 kb 95 kb 21.49%
5.jpg 5493 kb 111 kb 91 kb 18.02%

平均压缩比是:27.24%

按照原图大小,不缩小分辨率,仅压缩质量。

文件名 照片原图 压缩后jpg 压缩后webp 压缩比
3.jpg 4751 kb 796 kb 426 kb 46.48%

至此,我们就非常方便的使用了webp来对图片进行更加极致的压缩,兼顾了图片体积和质量。

呃,csdn不支持上传webp的图片。你们可以看压缩包。 
我嫌麻烦,可能不会传压缩包了……所以,你们看截图吧~

睁大眼睛对比一下有啥区别,不缩小分辨率,仅压缩质量,这个3.jpg可是有46.48%的压缩比噢。 
这个场景是晚上在灯光充足的室内吃饭拍的。

图片另存为放大看

这里写图片描述
这里写图片描述

5. 用Gzip再压缩

刚刚是针对本地图片的压缩,接下来,我们需要将图片传输到服务器。这个过程依然有优化空间,就是利用Gzip。

Gzip的作用对象是整个请求体,具体来说是对请求体中的内容进行可逆的压缩,类似pc上zip的那种。

Gzip压缩的请求体,需要加入相应的header: 「Content-Encoding:gzip」。 
这事情Retrofit会帮你做好。

后台服务器接收到在此类型的请求,就会对请求体解压,因此需要后端的支持。

另外要注意的是,Gzip针对比较大的请求体压缩效果不错,尤其是未经过压缩的纯文本类型。

如果请求本来就很小,那么就不要使用gzip压缩了,压缩包自己的元数据可能比你的请求体还大,得不偿失。你可以自己测试一下,我估计zip和gzip的压缩字典比较类似,可以直接在pc上做测试。

6. Retrofit对请求Gzip压缩

网络请求方面,我项目里使用Retrofit (OKHttp) + RxJava。

Retrofit的Gzip压缩,本质上是通过OKHttp的拦截器来完成的。 
0拦截请求 
1加入header 
2压缩请求 
3发送出去

搞定,方便。

https://github.com/square/okhttp/wiki/Interceptors

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> class GzipRequestInterceptor implements Interceptor {
  <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Response <span class="hljs-title" style="box-sizing: border-box;">intercept</span>(Interceptor.Chain chain) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException {
    Request originalRequest = chain.request();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (originalRequest.body() == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> || originalRequest.header(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Content-Encoding"</span>) != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> chain.proceed(originalRequest);
    }
    Request compressedRequest = originalRequest.newBuilder()
        .header(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Content-Encoding"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"gzip"</span>)
        .method(originalRequest.method(), gzip(originalRequest.body()))
        .build();
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> chain.proceed(compressedRequest);
  }
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> RequestBody <span class="hljs-title" style="box-sizing: border-box;">gzip</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> RequestBody body) {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RequestBody() {
      <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> MediaType <span class="hljs-title" style="box-sizing: border-box;">contentType</span>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> body.contentType();
      }
      <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> <span class="hljs-title" style="box-sizing: border-box;">contentLength</span>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// We don't know the compressed length in advance!</span>
      }
      <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">writeTo</span>(BufferedSink sink) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throws</span> IOException {
        BufferedSink gzipSink = Okio.buffer(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> GzipSink(sink));
        body.writeTo(gzipSink);
        gzipSink.close();
      }
    };
  }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul>

7. 请求体抓包对比

我会用Fiddler4 监测整个请求过程,以方便我们得知实际传输了多大的数据。

上传的具体代码就不发了,这个不是重点。 
我把请求抓包之后,把request这个保存下来。 
这里写图片描述

这是同时上传两张图片的大小

文件名 请求体大小
WebP+Gzip 169kb
WebP 222kb

用Gzip压缩 比不加Gzip 又小 23% ! jpg我就不发了,可以按照前面的估算一下~

WebP比Jpg小27%,然后gzip+webp又比单纯的webp小23%,节省下来的流量多可观啊!

8. 最后 
WebP默认只支持Android 4.0以上,现在项目最低支持的版本是16,所以没什么问题。如果你的项目最低要支持到2.0,好像也有第三方支持,但是我建议你抓产品出去打一顿。

在我们的项目里,iOS没用使用该技术。

根据下面的参考链接的数据以及我本人测试数据来看,WebP不仅大大的节省了用户的流量,同时还可以加速图片传输速度。就照片传输的角度来看,是非常有利的。 
如果还是有人告诉你:“IOS做不了,不做了。”,“后台XXX不做了。” 
我建议你抓产品出去打一顿。


相关参考:

http://isux.tencent.com/introduction-of-webp.html(产品经理看这个)

http://blog.csdn.net/GeekLei/article/details/41147479 (后台需要看这个)

https://developers.google.com/speed/webp/ 
http://www.infoq.com/cn/articles/sdk-optimazation?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term (他们表示IOS也没有用上) 
http://blog.csdn.net/mingchunhu/article/details/8155742

(Android全部都要看)


update 2016年5月27日20:03:21:

刚刚竟然有Android表示“IOS做不了,不做了。” 
那你就太窝囊了,人家有迁就过你吗?(微笑)

原文出处:http://blog.csdn.net/a405942873/article/details/51498833
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值