Android开发之图片压缩与传输的方法
白羽 2018-06-27 来源 :网络 阅读 968 评论 0

摘要:本文将带你了解Android开发之 图片压缩与传输的方法,希望本文对大家学Android有所帮助。


1. 简介

直到4g时代,流量依然是宝贵的东西。而移动网络传输中,最占流量的一种载体:图片,成为了我们移动开发者不得不关注的一个问题。

我们关注的问题,无非是图片体积和质量如何达到一个比较和谐的平衡,希望得到质量不错的图片同时体积还不能太大。

走在时代前列的谷歌给出了一个不错的答案——WebP。

WebP是一种图片文件格式,在相同的压缩指标下,webp的有损压缩能比jpg小 25-34%。而在我自己的测试里,有时候能小50%。

2. 大企业背书

WebP在2010年发布第一个版本,到现在已经6年了,谷歌旗下的各种网站G+、以及非常有代表性的YouTube,他的视频文件格式WebM就是基于WebP构造的。

据说腾讯、淘宝、美团也有部分应用。

3. Android 端 JPG 转换 WebP

RxJava线程转换:

    String[] imgs = new String[]{"1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg"};

    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures/test/";

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);//      test = Api.getBuilder().create(Test.class);

        String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE

                , Manifest.permission.READ_PHONE_STATE

                , Manifest.permission.CAMERA};

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            requestPermissions(permissions, 0);

        }

        compress();

    }

    private void compress() {

        Observable.from(imgs)

                .subscribeOn(Schedulers.io())

                .doOnNext(new Action1<String>() {

                    @Override

                    public void call(String imgName) {

                        compress(imgName);

                    }

                })

                .subscribe();

    }

    private void compress(String imgName) {

        try {

            File file = new File(path, imgName);

            Log.i("compress", "jpg start");

            byte[] bytes = BitmapUtil.compressBitmapToBytes(file.getPath(), 600, 0, 60, Bitmap.CompressFormat.JPEG);

            File jpg = new File(path, imgName + "compress.jpg");

            FileUtils.writeByteArrayToFile(jpg, bytes);

            Log.i("compress", "jpg finish");

            Log.i("compress", "----------------------------------------------------");

            Log.i("compress", "webp start");

            byte[] bytes1 = BitmapUtil.compressBitmapToBytes(file.getPath(), 600, 0, 60, Bitmap.CompressFormat.WEBP);//分别是图片路径,宽度高度,质量,和图片类型,重点在这里。

            File webp = new File(path, imgName + "compress.webp");

            FileUtils.writeByteArrayToFile(webp, bytes1);

            Log.i("compress", "webp finish");

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

我的测试机器也是Oneplus 1 ,CM13,所以需要获取相应的权限。

利用RxJava来做线程操作,在io线程里做了耗时操作。

public static byte[] compressBitmapToBytes(String filePath, int reqWidth, int reqHeight, int quality, Bitmap.CompressFormat format) {

        Bitmap bitmap = getSmallBitmap(filePath, reqWidth, reqHeight);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        bitmap.compress(format, quality, baos);

        byte[] bytes = baos.toByteArray();

        bitmap.recycle();

        Log.i(TAG, "Bitmap compressed success, size: " + bytes.length);

        return bytes;

    }

    public static Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) {

        BitmapFactory.Options options = new BitmapFactory.Options();

        options.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(filePath, options);

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

        options.inJustDecodeBounds = false;//      options.inPreferQualityOverSpeed = true;

        return BitmapFactory.decodeFile(filePath, options);

    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

        int h = options.outHeight;

        int w = options.outWidth;

        int inSampleSize = 0;

        if (h > reqHeight || w > reqWidth) {

            float ratioW = (float) w / reqWidth;

            float ratioH = (float) h / reqHeight;

            inSampleSize = (int) Math.min(ratioH, ratioW);

        }

        inSampleSize = Math.max(1, inSampleSize);

        return inSampleSize;

    }

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

4. WebP对比

我用我日常生活里拍下的照片来做简单的对比测试,不是特别严谨,仅供简单的参考。

拍照设备是刷了CM13的 一加 1 。拍照场景都是日常生活特别常见的。


 

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

Android开发之图片压缩与传输的方法

   

平均压缩比是:27.24%

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

Android开发之图片压缩与传输的方法



   

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




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发送出去


/** This interceptor compresses the HTTP request body. Many webservers can't handle this! */final class GzipRequestInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request originalRequest = chain.request();
    if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
      return chain.proceed(originalRequest);
    }
    Request compressedRequest = originalRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .method(originalRequest.method(), gzip(originalRequest.body()))
        .build();
    return chain.proceed(compressedRequest);
  }
  private RequestBody gzip(final RequestBody body) {
    return new RequestBody() {
      @Override public MediaType contentType() {
        return body.contentType();
      }
      @Override public long contentLength() {
        return -1; // We don't know the compressed length in advance!
      }
      @Override public void writeTo(BufferedSink sink) throws IOException {
        BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
        body.writeTo(gzipSink);
        gzipSink.close();
      }
    };
  }
}

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,好像也有第三方支持。


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程