Android性能优化 中

7.Android性能优化典范-第4季

5.数据呈现的顺序以及结构会对序列化之后的空间产生不小的影响

Android性能优化 中

改变数据结构对内存及gzip的影响

  1. gzip
    1. gzip概念:HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术
      • 一般对纯文本内容可压缩到原大小的40%
      • 减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间
    2. okHttp对gzip的支持
      1. okHttp支持gzip自动解压缩,不需要设置Accept-Encoding为gzip
        1. 开发者手动设置Accept-Encoding,okHttp不负责解压缩
        2. 开发者没有设置Accept-Encoding时,则自动添加Accept-Encoding: gzip,自动添加的request,response支持自动解压
          1. 自动解压时移除Content-Length,所以上层Java代码想要contentLength时为-1
          2. 自动解压时移除 Content-Encoding
          3. 自动解压时,如果是分块传输编码,Transfer-Encoding: chunked不受影响
  2. 我们在向服务器提交大量数据的时候,希望对post的数据进行gzip压缩,需要使用自定义拦截器
  3. import okhttp3.Interceptor;
    import okhttp3.MediaType;
    import okhttp3.Request;
    import okhttp3.RequestBody;
    import okhttp3.Response;
    import okio.BufferedSink;
    import okio.GzipSink;
    import okio.Okio;
    
    public class GzipRequestInterceptor implements Interceptor {
        @Override
        public Response intercept(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; // 无法提前知道压缩后的数据大小
                }
    
                @Override
                public void writeTo(BufferedSink sink) throws IOException {
                    BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                    body.writeTo(gzipSink);
                    gzipSink.close();
                }
            };
        }
    }
    
    然后构建OkhttpClient的时候,添加拦截器:
    OkHttpClient okHttpClient = new OkHttpClient.Builder() 
        .addInterceptor(new GzipRequestInterceptor())//开启Gzip压缩
        ...
        .build();

     

2.改变数据结构,就是将原始集合按照集合中对象的属性进行拆分,变成多个属性集合的形式.

  1. 改变数据结构后JSON字符串长度有明显降低
  2. 使用GZIP对JSON字符串进行压缩,在原始集合中元素数据重复率逐渐变大的情况下,GZIP压缩后的原始JSON字符串长度/GZIP压缩后的改变数据结构的JSON字符串会明显大于1.
  3. 代码及测试结果如下,结果仅供参考(ZipUtils是网上荡的,且没有使用网上用过的Base64Decoder,Base64Encoder)

 

public final class ZipUtils {

    /**
     * Gzip 压缩数据
     *
     * @param unGzipStr
     * @return
     */
    public static String compressForGzip(String unGzipStr) {

//        if (TextUtils.isEmpty(unGzipStr)) {
//            return null;
//        }
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPOutputStream gzip = new GZIPOutputStream(baos);
            gzip.write(unGzipStr.getBytes());
            gzip.close();
            byte[] encode = baos.toByteArray();
            baos.flush();
            baos.close();
//            return Base64Encoder.encode(encode);
            return new String(encode);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Gzip解压数据
     *
     * @param gzipStr
     * @return
     */
    public static String decompressForGzip(String gzipStr) {
//        if (TextUtils.isEmpty(gzipStr)) {
//            return null;
//        }
//        byte[] t = Base64Decoder.decodeToBytes(gzipStr);
        byte[] t = gzipStr.getBytes();
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(t);
            GZIPInputStream gzip = new GZIPInputStream(in);
            byte[] buffer = new byte[1024];
            int n = 0;
            while ((n = gzip.read(buffer, 0, buffer.length)) > 0) {
                out.write(buffer, 0, n);
            }
            gzip.close();
            in.close();
            out.close();
            return out.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * Zip 压缩数据
     *
     * @param unZipStr
     * @return
     */
    public static String compressForZip(String unZipStr) {

//        if (TextUtils.isEmpty(unZipStr)) {
//            return null;
//        }
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ZipOutputStream zip = new ZipOutputStream(baos);
            zip.putNextEntry(new ZipEntry("0"));
            zip.write(unZipStr.getBytes());
            zip.closeEntry();
            zip.close();
            byte[] encode = baos.toByteArray();
            baos.flush();
            baos.close();
//            return Base64Encoder.encode(encode);
            return new String(encode);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Zip解压数据
     *
     * @param zipStr
     * @return
     */
    public static String decompressForZip(String zipStr) {

//        if (TextUtils.isEmpty(zipStr)) {
//            return null;
//        }
//        byte[] t = Base64Decoder.decodeToBytes(zipStr);
        byte[] t = zipStr.getBytes();
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(t);
            ZipInputStream zip = new ZipInputStream(in);
            zip.getNextEntry();
            byte[] buffer = new byte[1024];
            int n = 0;
            while ((n = zip.read(buffer, 0, buffer.length)) > 0) {
                out.write(buffer, 0, n);
            }
            zip.close();
            in.close();
            out.close();
            return out.toString("UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class GzipZipTest {
    public static void main(String[] args) {
        GzipZipTest t = new GzipZipTest();
        t.t();
    }
    private void t(){
        /*List l = new ArrayList();
        for(int i=0;i<1;i++){
            for(int j=0;j<6000;j++){
                Person p = new Person();
                p.age = j;
                p.gender = "gender"+j;
                p.name = "name"+j;
                l.add(p);
            }
        }
        Gson gson = new Gson();
        List names = new ArrayList();
        List genders = new ArrayList();
        List ages = new ArrayList();
        for(Person p:l){
            names.add(p.name);
            genders.add(p.gender);
            ages.add(p.age);
        }
        PersonItemList itemList = new PersonItemList();
        itemList.items = l;
        String jsonDataOri = gson.toJson(itemList);
        System.out.println("原始数据结构 压缩前json数据长度 ---->" + jsonDataOri.length());
        PersonAttrList attrList = new PersonAttrList();
        attrList.names = names;
        attrList.genders = genders;
        attrList.ages = ages;
        String jsonDataVariety = gson.toJson(attrList);
        System.out.println("变种数据结构 压缩前json数据长度 ---->" + jsonDataVariety.length());
        System.out.println("===================================================");

        for(int i=0;i" + (end - start));
            System.out.println("原始数据结构 Gzip压缩后json数据长度 ---->" + gzipStr.length());
            //Zip压缩
//        start = System.currentTimeMillis();
//        String zipStr = ZipUtils.compressForZip(jsonDataOri);
//        end = System.currentTimeMillis();
//        System.out.println("原始数据结构 Zip压缩耗时 cost time---->" + (end - start));
//        System.out.println("原始数据结构 Zip压缩后json数据长度 ---->" + zipStr.length());

            //2:变种数据结构
            //Gzip压缩
            start = System.currentTimeMillis();
            String gzipStrVariety = ZipUtils.compressForGzip(jsonDataVariety);
            end = System.currentTimeMillis();
            System.out.println("变种数据结构 Gzip压缩耗时 cost time---->" + (end - start));
            System.out.println("变种数据结构 Gzip压缩后json数据长度 ---->" + gzipStrVariety.length());
            //Zip压缩
//        start = System.currentTimeMillis();
//        String zipStrVariety = ZipUtils.compressForZip(jsonDataVariety);
//        end = System.currentTimeMillis();
//        System.out.println("变种数据结构 Zip压缩耗时 cost time---->" + (end - start));
//        System.out.println("变种数据结构 Zip压缩后json数据长度 ---->" + zipStrVariety.length());
            System.out.println("压缩后 原始结构长度:变种数据结构="+((float)gzipStr.length())/(float)gzipStrVariety.length());
            System.out.println("===================================================");
        }*/

        float repetitionRatio = 0.00F;
        List l = new ArrayList();
        for(repetitionRatio = 0.000F; repetitionRatio < 0.500F; repetitionRatio+=0.005F){
            int reportIndex = (int) (6000 * (1-repetitionRatio));
            for(int i = 0;i 0.00F){
                int reportCount = (int) (6000 * repetitionRatio);
                for(int i = 0;i<reportCount;i++){
                    Person p = new Person();
                    p.age = i;
                    p.gender = "gender"+i;
                    p.name = "name"+i;
                    l.add(p);
                }
            }
            Gson gson = new Gson();
            List names = new ArrayList();
            List genders = new ArrayList();
            List ages = new ArrayList();
            for(Person p:l){
                names.add(p.name);
                genders.add(p.gender);
                ages.add(p.age);
            }
            PersonItemList itemList = new PersonItemList();
            itemList.items = l;
            String jsonDataOri = gson.toJson(itemList);
            System.out.println("===================================================");
            System.out.println("原始数据结构 压缩前json数据长度 ---->" + jsonDataOri.length());
            PersonAttrList attrList = new PersonAttrList();
            attrList.names = names;
            attrList.genders = genders;
            attrList.ages = ages;
            String jsonDataVariety = gson.toJson(attrList);
            System.out.println("变种数据结构 压缩前json数据长度 ---->" + jsonDataVariety.length());
            //1:原始数据结构
            //Gzip压缩
            long start = System.currentTimeMillis();
            String gzipStr = ZipUtils.compressForGzip(jsonDataOri);
            long end = System.currentTimeMillis();
            System.out.println("原始数据结构 Gzip压缩后json数据长度 ---->" + gzipStr.length());

            //2:变种数据结构
            //Gzip压缩
            start = System.currentTimeMillis();
            String gzipStrVariety = ZipUtils.compressForGzip(jsonDataVariety);
            end = System.currentTimeMillis();
            System.out.println("变种数据结构 Gzip压缩后json数据长度 ---->" + gzipStrVariety.length());
            System.out.println("重复率为 "+repetitionRatio/(1-repetitionRatio)+" 压缩后:原始结构长度:变种数据结构="+((float)gzipStr.length())/(float)gzipStrVariety.length());
        }
    }
    public class Person implements Serializable{
        public String name;
        public String gender;
        public int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getGender() {
            return gender;
        }

        public void setGender(String gender) {
            this.gender = gender;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
    public class PersonItemList implements Serializable{
        public List items;

        public List getItems() {
            return items;
        }

        public void setItems(List items) {
            this.items = items;
        }
    }
    public class PersonAttrList implements Serializable{
        public List names;
        public List genders;
        public List ages;

        public List getNames() {
            return names;
        }

        public void setNames(List names) {
            this.names = names;
        }

        public List getGenders() {
            return genders;
        }

        public void setGenders(List genders) {
            this.genders = genders;
        }

        public List getAges() {
            return ages;
        }

        public void setAges(List ages) {
            this.ages = ages;
        }
    }
}

首先看当单个对象属性重复率超过100%的情况下打印结果:

List l = new ArrayList();
        for(int i=0;i<1;i++){
            for(int j=0;j273011  //因为i和j变动,数据会略有变化
变种数据结构 压缩前json数据长度 ---->129032  //因为i和j变动,数据会略有变化

i=x;    j=y;

//x=1,j=6000:代表数据没有任何重复的情形
x=1;    j=6000;
原始数据结构 Gzip压缩后json数据长度 ---->44215
变种数据结构 Gzip压缩后json数据长度 ---->39561
压缩后 原始结构长度:变种数据结构=1.1176411

//x=2,j=3000:代表每个对象都存在另1个属性完全一致的对象.单个对象重复率100%
x=2;    j=3000;
原始数据结构 Gzip压缩后json数据长度 ---->44204
变种数据结构 Gzip压缩后json数据长度 ---->27628
压缩后 原始结构长度:变种数据结构=1.599971

//余下的代表每单个对象重复率超过100%的情况
x=3;    j=2000;
原始数据结构 Gzip压缩后json数据长度 ---->43733
变种数据结构 Gzip压缩后json数据长度 ---->17020
压缩后 原始结构长度:变种数据结构=2.5695064

x=4;    j=1500;
原始数据结构 Gzip压缩后json数据长度 ---->43398
变种数据结构 Gzip压缩后json数据长度 ---->13914
压缩后 原始结构长度:变种数据结构=3.119017

x=6;    j=1000;
原始数据结构 Gzip压缩后json数据长度 ---->42166
变种数据结构 Gzip压缩后json数据长度 ---->8016
压缩后 原始结构长度:变种数据结构=5.2602296

x=7;    j=857;
原始数据结构 Gzip压缩后json数据长度 ---->41743
变种数据结构 Gzip压缩后json数据长度 ---->7024
压缩后 原始结构长度:变种数据结构=5.94291

x=8;    j=750;
原始数据结构 Gzip压缩后json数据长度 ---->41561
变种数据结构 Gzip压缩后json数据长度 ---->6378
压缩后 原始结构长度:变种数据结构=6.516306

x=9;    j=667;
原始数据结构 Gzip压缩后json数据长度 ---->41491
变种数据结构 Gzip压缩后json数据长度 ---->5870
压缩后 原始结构长度:变种数据结构=7.0683136

x=10;   j=600;
原始数据结构 Gzip压缩后json数据长度 ---->7552
变种数据结构 Gzip压缩后json数据长度 ---->5503
压缩后 原始结构长度:变种数据结构=1.3723423

x=12;   j=500;
原始数据结构 Gzip压缩后json数据长度 ---->6955
变种数据结构 Gzip压缩后json数据长度 ---->4962
压缩后 原始结构长度:变种数据结构=1.4016526

x=15;   j=400;
原始数据结构 Gzip压缩后json数据长度 ---->6207
变种数据结构 Gzip压缩后json数据长度 ---->4179
压缩后 原始结构长度:变种数据结构=1.4852836

x=20;   j=300;
原始数据结构 Gzip压缩后json数据长度 ---->5117
变种数据结构 Gzip压缩后json数据长度 ---->3576
压缩后 原始结构长度:变种数据结构=1.4309285

x=30;   j=200;
原始数据结构 Gzip压缩后json数据长度 ---->4511
变种数据结构 Gzip压缩后json数据长度 ---->3156
压缩后 原始结构长度:变种数据结构=1.429341

x=40;   j=150;
原始数据结构 Gzip压缩后json数据长度 ---->4359
变种数据结构 Gzip压缩后json数据长度 ---->3035
压缩后 原始结构长度:变种数据结构=1.4362438

x=60;   j=100;
原始数据结构 Gzip压缩后json数据长度 ---->2832
变种数据结构 Gzip压缩后json数据长度 ---->1382
压缩后 原始结构长度:变种数据结构=2.049204

x=80;   j=75;
原始数据结构 Gzip压缩后json数据长度 ---->2581
变种数据结构 Gzip压缩后json数据长度 ---->1217
压缩后 原始结构长度:变种数据结构=2.1207888

x=150;  j=40;
原始数据结构 Gzip压缩后json数据长度 ---->1835
变种数据结构 Gzip压缩后json数据长度 ---->890
压缩后 原始结构长度:变种数据结构=2.0617979

x=200;  j=30;
原始数据结构 Gzip压缩后json数据长度 ---->1744
变种数据结构 Gzip压缩后json数据长度 ---->797
压缩后 原始结构长度:变种数据结构=2.1882057

x=300;  j=20;
原始数据结构 Gzip压缩后json数据长度 ---->1539
变种数据结构 Gzip压缩后json数据长度 ---->739
压缩后 原始结构长度:变种数据结构=2.082544

x=316;  j=19;
原始数据结构 Gzip压缩后json数据长度 ---->1269
变种数据结构 Gzip压缩后json数据长度 ---->725
压缩后 原始结构长度:变种数据结构=1.7503449

x=400;  j=15;
原始数据结构 Gzip压缩后json数据长度 ---->1488
变种数据结构 Gzip压缩后json数据长度 ---->662
压缩后 原始结构长度:变种数据结构=2.247734

x=500;  j=12;
原始数据结构 Gzip压缩后json数据长度 ---->1453
变种数据结构 Gzip压缩后json数据长度 ---->563
压缩后 原始结构长度:变种数据结构=2.580817

x=600;  j=10;
原始数据结构 Gzip压缩后json数据长度 ---->1044
变种数据结构 Gzip压缩后json数据长度 ---->573
压缩后 原始结构长度:变种数据结构=1.8219895

x=667;  j=9;
原始数据结构 Gzip压缩后json数据长度 ---->1291
变种数据结构 Gzip压缩后json数据长度 ---->527
压缩后 原始结构长度:变种数据结构=2.4497154

x=750;  j=8;
原始数据结构 Gzip压缩后json数据长度 ---->1155
变种数据结构 Gzip压缩后json数据长度 ---->520
压缩后 原始结构长度:变种数据结构=2.2211537

x=1000; j=6;
原始数据结构 Gzip压缩后json数据长度 ---->1269
变种数据结构 Gzip压缩后json数据长度 ---->429
压缩后 原始结构长度:变种数据结构=2.958042

x=1200; j=5;
原始数据结构 Gzip压缩后json数据长度 ---->1135
变种数据结构 Gzip压缩后json数据长度 ---->478
压缩后 原始结构长度:变种数据结构=2.374477

x=3000; j=2;
原始数据结构 Gzip压缩后json数据长度 ---->990
变种数据结构 Gzip压缩后json数据长度 ---->382
压缩后 原始结构长度:变种数据结构=2.591623

x=6000; j=1;
原始数据结构 Gzip压缩后json数据长度 ---->590
变种数据结构 Gzip压缩后json数据长度 ---->311
压缩后 原始结构长度:变种数据结构=1.897106

当每个对象属性重复率低于100%的情况下打印结果:
===================================================
原始数据结构 压缩前json数据长度 ---->314681
变种数据结构 压缩前json数据长度 ---->170702
原始数据结构 Gzip压缩后json数据长度 ---->44215
变种数据结构 Gzip压缩后json数据长度 ---->39561
重复率为 0.0 压缩后:原始结构长度:变种数据结构=1.1176411
===================================================
原始数据结构 压缩前json数据长度 ---->629141
变种数据结构 压缩前json数据长度 ---->341162
原始数据结构 Gzip压缩后json数据长度 ---->88279
变种数据结构 Gzip压缩后json数据长度 ---->66875
重复率为 0.0050251256 压缩后:原始结构长度:变种数据结构=1.3200598
===================================================
原始数据结构 压缩前json数据长度 ---->943421
变种数据结构 压缩前json数据长度 ---->511442
原始数据结构 Gzip压缩后json数据长度 ---->131892
变种数据结构 Gzip压缩后json数据长度 ---->90806
重复率为 0.01010101 压缩后:原始结构长度:变种数据结构=1.4524591
===================================================
原始数据结构 压缩前json数据长度 ---->1257521
变种数据结构 压缩前json数据长度 ---->681542
原始数据结构 Gzip压缩后json数据长度 ---->175554
变种数据结构 Gzip压缩后json数据长度 ---->116973
重复率为 0.015228426 压缩后:原始结构长度:变种数据结构=1.5008079
===================================================
原始数据结构 压缩前json数据长度 ---->1571501
变种数据结构 压缩前json数据长度 ---->851522
原始数据结构 Gzip压缩后json数据长度 ---->218945
变种数据结构 Gzip压缩后json数据长度 ---->142129
重复率为 0.020408163 压缩后:原始结构长度:变种数据结构=1.5404668
===================================================
原始数据结构 压缩前json数据长度 ---->1885341
变种数据结构 压缩前json数据长度 ---->1021386
原始数据结构 Gzip压缩后json数据长度 ---->262306
变种数据结构 Gzip压缩后json数据长度 ---->168725
重复率为 0.025641024 压缩后:原始结构长度:变种数据结构=1.5546362
===================================================
原始数据结构 压缩前json数据长度 ---->2199091
变种数据结构 压缩前json数据长度 ---->1191160
原始数据结构 Gzip压缩后json数据长度 ---->305678
变种数据结构 Gzip压缩后json数据长度 ---->191222
重复率为 0.030927831 压缩后:原始结构长度:变种数据结构=1.5985503
===================================================
原始数据结构 压缩前json数据长度 ---->2512751
变种数据结构 压缩前json数据长度 ---->1360844
原始数据结构 Gzip压缩后json数据长度 ---->348774
变种数据结构 Gzip压缩后json数据长度 ---->219050
重复率为 0.036269426 压缩后:原始结构长度:变种数据结构=1.5922118
===================================================
原始数据结构 压缩前json数据长度 ---->2826321
变种数据结构 压缩前json数据长度 ---->1530438
原始数据结构 Gzip压缩后json数据长度 ---->391506
变种数据结构 Gzip压缩后json数据长度 ---->243066
重复率为 0.041666664 压缩后:原始结构长度:变种数据结构=1.6106983
===================================================
原始数据结构 压缩前json数据长度 ---->3139801
变种数据结构 压缩前json数据长度 ---->1699942
原始数据结构 Gzip压缩后json数据长度 ---->434274
变种数据结构 Gzip压缩后json数据长度 ---->268432
重复率为 0.047120415 压缩后:原始结构长度:变种数据结构=1.6178175
****
===================================================
原始数据结构 压缩前json数据长度 ---->29936815
变种数据结构 压缩前json数据长度 ---->16114684
原始数据结构 Gzip压缩后json数据长度 ---->4187707
变种数据结构 Gzip压缩后json数据长度 ---->2014350
重复率为 0.9047608 压缩后:原始结构长度:变种数据结构=2.078937
===================================================
原始数据结构 压缩前json数据长度 ---->30248102
变种数据结构 压缩前json数据长度 ---->16281995
原始数据结构 Gzip压缩后json数据长度 ---->4232504
变种数据结构 Gzip压缩后json数据长度 ---->2034384
重复率为 0.92307574 压缩后:原始结构长度:变种数据结构=2.0804844
===================================================
原始数据结构 压缩前json数据长度 ---->30559389
变种数据结构 压缩前json数据长度 ---->16449306
原始数据结构 Gzip压缩后json数据长度 ---->4277046
变种数据结构 Gzip压缩后json数据长度 ---->2053854
重复率为 0.94174635 压缩后:原始结构长度:变种数据结构=2.082449
===================================================
原始数据结构 压缩前json数据长度 ---->30870676
变种数据结构 压缩前json数据长度 ---->16616617
原始数据结构 Gzip压缩后json数据长度 ---->4321134
变种数据结构 Gzip压缩后json数据长度 ---->2072485
重复率为 0.960783 压缩后:原始结构长度:变种数据结构=2.0850012
===================================================
原始数据结构 压缩前json数据长度 ---->31181963
变种数据结构 压缩前json数据长度 ---->16783928
原始数据结构 Gzip压缩后json数据长度 ---->4365924
变种数据结构 Gzip压缩后json数据长度 ---->2087159
重复率为 0.9801967 压缩后:原始结构长度:变种数据结构=2.0918024
===================================================
原始数据结构 压缩前json数据长度 ---->31493250
变种数据结构 压缩前json数据长度 ---->16951239
原始数据结构 Gzip压缩后json数据长度 ---->4409476
变种数据结构 Gzip压缩后json数据长度 ---->2100664
重复率为 0.9999986 压缩后:原始结构长度:变种数据结构=2.0990868