Android性能優化 筆記

說明

這篇文章是將好久以來看過的文章,包括本身寫的一些測試代碼的總結.屬於筆記的性質,沒有面面俱到,一些本身相對熟悉的點可能會略過.
最開始看到的性能優化的文章,就是胡凱的優化典範系列,後來又陸續看過一些人寫的,我的以爲anly_jun和胡凱的質量最好.
文章大的框架也是先把優化典範過一遍,記錄我的認爲重要的點,而後是anly_jun的系列,將以前未覆蓋的補充進去,也包括HenCoder的一些課程相關內容.
固然除了上面幾位,還有不少其餘大神的文章,時間久了也記不太清,在此一併謝過.javascript

筆記內容引用來源

  1. 胡凱
  2. anly_jun
  3. HenCoder

1.Android性能優化之渲染篇

1.VSYNC

  1. 幀率:GPU在1秒內繪製操做的幀數.如60fps.
    • 咱們一般都會提到60fps與16ms,這是由於人眼與大腦之間的協做沒法感知超過60fps的畫面更新.
    • 開發app的性能目標就是保持60fps,這意味着每一幀只有16ms=1000/60的時間來處理全部的任務
  2. 刷新率:屏幕在1秒內刷新屏幕的次數.如60Hz,每16ms刷新1次屏幕.
  3. GPU獲取圖形數據進行渲染,而後屏幕將渲染後的內容展現在屏幕上.
  4. 大多數手機屏幕的刷新率是60Hz,若是GPU渲染1幀的時間低於1000/60=16ms,那麼在屏幕刷新時候都有最新幀可顯示.若是GPU渲染某1幀 f 的時間超過16ms,在屏幕刷新時候,f並無被GPU渲染完成則沒法展現,屏幕只能繼續展現f的上1幀的內容.這就是掉幀,形成了UI界面的卡頓.
    下面展現了幀率正常和幀率低於刷新率(掉幀)的情形

2.GPU渲染:GPU渲染依賴2個組件:CPU和GPU

  1. CPU負責Measure,Layout,Record,Execute操做.
  2. GPU負責Rasterization(柵格化)操做.
    • Resterization柵格化是繪製那些Button,Shape,Path,String,Bitmap等組件最基礎的操做.它把組件拆分到不一樣的像素上進行顯示.這是一個很費時的操做.
    • CPU負責把UI組件計算成Polygons(多邊形),Texture(紋理),而後交給GPU進行柵格化渲染.
  3. 爲了App流暢,咱們須要確保在16ms內完成全部CPU和GPU的工做.

3.過分繪製

Overdraw過分繪製是指屏幕上的某個像素在同一幀的時間內被繪製了屢次.過分繪製會大量浪費CPU及GPU資源/佔用CPU和GPU的處理時間php

  • 過分繪製的緣由
    1. UI佈局存在大量重疊
    2. 非必須的背景重疊.
      • 如Activity有背景,Layout又有背景,子View又有背景.僅僅移除非必要背景就能夠顯著提高性能.
    3. 子View在onDraw中存在重疊部分繪製的狀況,好比Bitmap重疊繪製

4.如何提高渲染性能

  1. 移除XML佈局文件中非必要的Background
  2. 保持佈局扁平化,儘可能避免佈局嵌套
  3. 在任什麼時候候都避免調用requestLayout(),調用requestLayout會致使該layout的全部父節點都發生從新layout的操做
  4. 在自定義View的onDraw中避免過分繪製.
    代碼實例:
public class OverdrawView extends View {
    public OverdrawView(Context context) {
        super(context);
        init();
    }

    public OverdrawView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public OverdrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Bitmap bitmap1,bitmap2,bitmap3;
    private void init(){
        paint.setStyle(Paint.Style.FILL);
        bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.png1);
        bitmap2 = BitmapFactory.decodeResource(getResources(),R.mipmap.png2);
        bitmap3 = BitmapFactory.decodeResource(getResources(),R.mipmap.png3);
    }

    int w,h;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        w = getMeasuredWidth();
        h = getMeasuredHeight();
    }

    private boolean Overdraw = true;
    @Override
    protected void onDraw(Canvas canvas) {
        if(Overdraw){
            //默認會出現過分繪製
            canvas.drawBitmap(bitmap1,0,0,paint);
            canvas.drawBitmap(bitmap2,w/3,0,paint);
            canvas.drawBitmap(bitmap3,w*2/3,0,paint);
        }else{
            //使用Canvas.clipRect避免過分繪製
            canvas.save();
            canvas.clipRect(0,0,w/3,h);
            canvas.drawBitmap(bitmap1,0,0,paint);
            canvas.restore();
            canvas.save();
            canvas.clipRect(w/3,0,w*2/3,h);
            canvas.drawBitmap(bitmap2,w/3,0,paint);
            canvas.restore();
            canvas.save();
            canvas.clipRect(w*2/3,0,w,h);
            canvas.drawBitmap(bitmap3,w*2/3,0,paint);
            canvas.restore();
        }
    }
    //切換是否避免過分繪製
    public void toggleOverdraw(){
        Overdraw = !Overdraw;
        invalidate();
    }
}
複製代碼


效果圖:html

過分繪製

避免過分繪製

2.Android性能優化以內存篇

1.Android虛擬機的 分代堆內存/Generational Heap Memory模型

  1. 和JVM不一樣:Android的堆內存多了1個永久代/Permanent Generation.
  2. 和JVM相似:
    1. 新建立的對象存儲在新生代/Young Generation
    2. GC所佔用的時間和它是哪個Generation有關,Young Generation的每次GC操做時間是最短的,Old Generation其次,Permanent Generation最長
    3. 不管哪一代,觸發GC後,全部非垃圾回收線程暫停,GC結束後全部線程恢復執行
  3. 若是短期內進行過多GC,屢次暫停線程進行垃圾回收的累積時間就會增大.佔用過多的幀間隔時間/16ms,致使CPU和GPU用於計算渲染的時間不足,致使卡頓/掉幀.

2.內存泄漏和內存溢出

內存泄漏就是無用對象佔據的內存空間沒有及時釋放,致使內存空間浪費的狀況.memory leak.
內存溢出是App爲1個對象申請內存空間,內存空間不足的狀況.out of memory.
內存泄漏數量足夠大,就會引發內存溢出.或者說內存泄漏是內存溢出的緣由之一.java

3.Android性能優化典範-第2季

1.提高動畫性能

  1. Bitmap的縮放,旋轉,裁剪比較耗性能.例如在一個圓形的鐘表圖上,咱們把時鐘的指針摳出來當作單獨的圖片進行旋轉會比旋轉一張完整的圓形圖性能好.
  2. 儘可能減小每次重繪的元素能夠極大提高性能.能夠把複雜的View拆分會更小的View進行組合,在須要刷新界面時候僅對指定View進行重繪.
    • 假如鐘錶界面上有不少組件,能夠把這些組件作拆分,背景圖片單獨拎出來設置爲一個獨立的View,經過setLayerType()方法使得這個View強制用Hardware來進行渲染.至於界面上哪些元素須要作拆分,他們各自的更新頻率是多少,須要有針對性的單獨討論

2.對象池

  1. 短期內大量對象被建立而後很快被銷燬,會屢次觸發Android虛擬機在Young generation進行GC,使用AS查看內存曲線,會看到內存曲線劇烈起伏,稱爲"內存抖動".
  2. GC會暫停其餘線程,短期屢次GC/內存抖動會引發CPU和GPU在16ms內沒法完成當前幀的渲染,引發界面卡頓.
  3. 避免內存抖動,可使用對象池
    • 對象池的做用:減小頻繁建立和銷燬對象帶來的成本,實現對象的緩存和複用
    • 1 2 3 4
  4. 實例
    public class User {
        public String id;
        public String name;
        //對象池實例
        private static final SynchronizedPool<User> sPool = new SynchronizedPool<User>(10);
    
        public static User obtain() {
            User instance = sPool.acquire();
            return (instance != null) ? instance : new User();
        }
        public void recycle() {
            sPool.release(this);
        }
    }
    複製代碼

3.for index,for simple,iterator三種遍歷性能比較

public class ForTest {
    public static void main(String[] args) {
        Vector<Integer> v = new Vector<>();
        ArrayList<Integer> a = new ArrayList<>();
        LinkedList<Integer> l = new LinkedList<>();
        int time = 1000000;
        for(int i = 0; i< time; i++){
            Integer item = new Random().nextInt(time);
            v.add(item);
            a.add(item);
            l.add(item);
        }
        //測試3種遍歷性能
        long start = System.currentTimeMillis();
        for(int i = 0;i<v.size();i++){
            Integer item = v.get(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("for index Vector耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(int i = 0;i<a.size();i++){
            Integer item = a.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("for index ArrayList耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(int i = 0;i<l.size();i++){
            Integer item = l.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("for index LinkedList耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Integer item:v){
            Integer i = item;
        }
        end = System.currentTimeMillis();
        System.out.println("for simple Vector耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Integer item:a){
            Integer i = item;
        }
        end = System.currentTimeMillis();
        System.out.println("for simple ArrayList耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Integer item:l){
            Integer i = item;
        }
        end = System.currentTimeMillis();
        System.out.println("for simple LinkedList耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Iterator i = v.iterator();i.hasNext();){
            Integer item = (Integer) i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("for Iterator Vector耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Iterator i = a.iterator();i.hasNext();){
            Integer item = (Integer) i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("for Iterator ArrayList耗時:"+(end-start)+"ms");
        start = System.currentTimeMillis();
        for(Iterator i = l.iterator();i.hasNext();){
            Integer item = (Integer) i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("for Iterator LinkedList耗時:"+(end-start)+"ms");
    }
}

打印結果:
for index Vector耗時:28ms
for index ArrayList耗時:14ms
LinkedList就不能用for index方式進行遍歷.
for simple Vector耗時:68ms
for simple ArrayList耗時:11ms
for simple LinkedList耗時:34ms
for Iterator Vector耗時:49ms
for Iterator ArrayList耗時:12ms
for Iterator LinkedList耗時:0ms
複製代碼
  1. 不要用for index去遍歷鏈表,由於LinkedList在get任何一個位置的數據的時候,都會把前面的數據走一遍.應該使用Iterator去遍歷
    1. get(0),直接拿到0位的Node0的地址,拿到Node0裏面的數據
    2. get(1),直接拿到0位的Node0的地址,從0位的Node0中找到下一個1位的Node1的地址,找到Node1,拿到Node1裏面的數據
    3. get(2),直接拿到0位的Node0的地址,從0位的Node0中找到下一個1位的Node1的地址,找到Node1,從1位的Node1中找到下一個2位的Node2的地址,找到Node2,拿到Node2裏面的數據
  2. Vector和ArrayList,使用for index遍歷效率較高

4.Merge:經過Merge減小1個View層級

  1. 能夠將merge當作1個ViewGroup v,若是v的類型和v的父控件的類型一致,那麼v其實不必存在,由於白白增長了佈局的深度.因此merge使用時必須保證merge中子控件所應該在的ViewGroup類型和merge所在的父控件類型一致.python

  2. Merge的使用場景有2個:android

    1. Activity的佈局文件的根佈局是FrameLayout,則將FrameLayout替換爲merge
      • 由於setContentView本質就是將佈局文件inflate後加載到了id爲android.id.content的FrameLayout上.
    2. merge做爲根佈局的佈局文件經過include標籤被引入其餘佈局文件中.這時候include所在的父控件,必須和merge所在的佈局文件"本來根佈局"一致.
  3. 代碼示例
    merge做爲根佈局的佈局文件,用於Activity的setContentView:git

    activity_merge.xml
    
    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="0dp" android:text="111111" />
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="100dp" android:layout_marginLeft="40dp" android:text="222222" />
    </merge>
    複製代碼

    merge做爲根佈局的佈局文件,被include標籤引入其餘佈局文件中:
    activity_merge_include.xml
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
        <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="merge被include引用" />
        <include layout="@layout/activity_merge" />
    </LinearLayout>
    複製代碼

5.使用.9.png做爲背景

  • 典型場景是1個ImageView須要添加1個背景圖做爲邊框.這樣邊框所在矩形的中間部分和實際顯示的圖片就好重疊發生Overdraw.
  • 能夠將背景圖製做成.9.png.和前景圖重疊部分設置爲透明.Android的2D渲染器會優化.9.png的透明區域.

6.減小透明區域對性能的影響

  • 不透明的View,顯示它只須要渲染一次;若是View設置了alpha值,會至少須要渲染兩次,性能很差
    • 設置透明度setAlpha的時候,會把當前view繪製到offscreen buffer中,而後再顯示出來.offscreen buffer是 一個臨時緩衝區,把View放進來並作透明度的轉化,而後顯示到屏幕上,這個過程性能差,因此應該儘可能避免這個過程
  • 如何避免使用offscreen buffer
    1. 對於不存在過分繪製的View,如沒有背景的TextView,就能夠直接設置文字顏色;ImageView設置圖片透明度setImageAlpha;自定義View設置繪製時的paint的透明度
    2. 若是是自定義View,肯定不存在過分繪製,能夠重寫hasOverlappingRendering返回false便可.這樣設置alpha時android會自動優化,避免使用offscreen buffer.
      @Override
      public boolean hasOverlappingRendering() {
          return false;
      }
      複製代碼
    3. 若是不是1,2兩種狀況,要設置View的透明度,則須要讓GPU來渲染指定View,而後再設置透明度.
      View v = findViewById(R.id.root);
      //經過setLayerType的方法來指定View應該如何進行渲染
      //開啓硬件加速
      v.setLayerType(View.LAYER_TYPE_HARDWARE,null);
      v.setAlpha(0.60F);
      //透明度設置完畢後關閉硬件加速
      v.setLayerType(View.LAYER_TYPE_NONE,null);
      複製代碼

4.Android性能優化典範-第3季

1.避免使用枚舉,用註解進行替代

  1. 枚舉的問題
    1. 每一個枚舉值都是1個對象,相比較Integer和String常量,枚舉的內存開銷至少是其2倍.
    2. 過多枚舉會增長dex大小及其中的方法數量,增長App佔用的空間及引起65536概率
  2. 如何替代枚舉:使用註解
    1. android.support.annotation中的@IntDef,@StringDef來包裝Integer和String常量.
    2. 3個步驟
      1. 首先定義常量
      2. 而後自定義註解,設置取值範圍就是剛剛定義的常量,並設置自定義註解的保留範圍爲源碼時/SOURCE
      3. 位指定的屬性及方法添加自定義註解.
    3. 代碼實例
      public class MainActivity extends Activity {
          //1:首先定義常量
          public static final int MALE = 0;
          public static final int FEMALE = 1;
          
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main_activity);
              Person person = new Person();
              person.setSex(MALE);
              ((Button) findViewById(R.id.test)).setText(person.getSexDes());
          }
          class Person {
              //3.爲指定的屬性及方法添加自定義註解
              @SEX
              private int sex;
              //3.爲指定的屬性及方法添加自定義註解
              public void setSex(@SEX int sex) {
                  this.sex = sex;
              }
              //3.爲指定的屬性及方法添加自定義註解
              @SEX
              public int getSex() {
                  return sex;
              }
              public String getSexDes() {
                  if (sex == MALE) {
                      return "男";
                  } else {
                      return "女";
                  }
              }
          }
          //2:而後建立自定義註解,設置取值範圍就是剛剛定義的常量,並設置自定義註解的保留範圍是源碼時
          @IntDef({MALE, FEMALE})
          @Retention(RetentionPolicy.SOURCE)
          public @interface SEX {
          }
      }
      複製代碼

5.Android內存優化之OOM

如何避免OOM:

  1. 減少對象的內存佔用
  2. 內存對象複用防止重建
  3. 避免內存泄漏
  4. 內存使用策略優化

1.減少對象的內存佔用

  1. 避免使用枚舉,用註解替代
  2. 減少建立的Bitmap的內存,使用合適的縮放比例及解碼格式
    1. inSampleSize:縮放比例
    2. decode format:解碼格式
  3. 如今不少圖片資源的URL均可以添加圖片尺寸做爲參數.在經過網絡獲取圖片時選擇合適的尺寸,減少網絡流量消耗,並減少生成的Bitmap的大小.

2.內存對象的重複利用

  1. 對象池技術:減小頻繁建立和銷燬對象帶來的成本,實現對象的緩存和複用
  2. 儘可能使用Android系統內置資源,可下降APK大小,在必定程度下降內存開銷
  3. ConvertView的複用
  4. LRU的機制實現Bitmap的緩存(圖片加載框架的必備機制)
  5. 在for循環中,用StringBuilder代替String實現字符串拼接

3.避免內存泄漏

  1. 在App中使用leakcanary檢測內存泄漏:leakcanary
  2. Activity的內存泄漏
    1. Handler引發Activity內存泄漏
      1. 緣由:Handler做爲Activity的1個非靜態內部類實例,持有Activity實例的引用.若Activity退出後Handler依然有待接收的Message,這時候發生GC,Message-Handler-Activity的引用鏈致使Activity沒法被回收.
      2. 2種解決方法
        1. 在onDestroy調用Handler.removeCallbacksAndMessages(null)移除該Handler關聯的全部Message及Runnable.再發生GC,Message已經不存在,就能夠順利的回收Handler及Activitygithub

          @Override
          protected void onDestroy() {
              super.onDestroy();
              m.removeCallbacksAndMessages(null);
          }
          複製代碼
        2. 自定義靜態內部類繼承Handler,靜態內部類實例不持有外部Activity的引用.在自定義Handler中定義外部Activity的弱引用,只有弱引用關聯的外部Activity實例未被回收的狀況下才繼續執行handleMessage.自定義Handler持有外部Activity的弱引用,發生GC時不耽誤Activity被回收.web

          static class M extends Handler{
                  WeakReference<Activity> mWeakReference;
                  public M(Activity activity) {
                      mWeakReference=new WeakReference<Activity>(activity);
                  }
                  @Override
                  public void handleMessage(Message msg) {
                      if(mWeakReference != null){
                          Activity activity=mWeakReference.get();
                          if(activity != null){
                              if(msg.what == 15){
                                  Toast.makeText(activity,"M:15",Toast.LENGTH_SHORT).show();
                              }
                              if(msg.what == 5){
                                  Toast.makeText(activity,"M:5",Toast.LENGTH_SHORT).show();
                              }
                          }
                      }
                  }
              }
              private M m;
              @Override
              protected void onResume() {
                  super.onResume();
                  m = new M(this);
                  m.sendMessageDelayed(m.obtainMessage(15),15000);
                  m.sendMessageDelayed(m.obtainMessage(5),5000);
              }
          複製代碼
        3. 在避免內存泄漏的前提下,若是要求Activity退出就不執行後續動做,用方法1.若是要求後續動做在GC發生前繼續執行,使用方法2算法

  3. Context:儘可能使用Application Context而不是Activity Context,避免不經意的內存泄漏
  4. 資源對象要及時關閉

4.內存使用策略優化

  1. 圖片選擇合適的文件夾進行存放
    • hdpi/xhdpi/xxhdpi等等不一樣dpi的文件夾下的圖片在不一樣的設備上會通過scale的處理。例如咱們只在hdpi的目錄下放置了一張100100的圖片,那麼根據換算關係,xxhdpi的手機去引用那張圖片就會被拉伸到200200。須要注意到在這種狀況下,內存佔用是會顯著提升的。對於不但願被拉伸的圖片,須要放到assets或者nodpi的目錄下
  2. 謹慎使用依賴注入框架.依賴注入框架會掃描代碼,須要大量的內存空間映射代碼.
  3. 混淆能夠減小沒必要要的代碼,類,方法等.下降映射代碼所需的內存空間
  4. onLowMemory()與onTrimMemory():沒想到應該怎麼用
    1. onLowMemory
      • 當全部的background應用都被kill掉的時候,forground應用會收到onLowMemory()的回調.在這種狀況下,須要儘快釋放當前應用的非必須的內存資源,從而確保系統可以繼續穩定運行
    2. onTrimMemory(int level)
      • 當系統內存達到某些條件的時候,全部正在運行的應用都會收到這個回調,同時在這個回調裏面會傳遞如下的參數,表明不一樣的內存使用狀況,收到onTrimMemory()回調的時候,須要根據傳遞的參數類型進行判斷,合理的選擇釋放自身的一些內存佔用,一方面能夠提升系統的總體運行流暢度,另外也能夠避免本身被系統判斷爲優先須要殺掉的應用

6.Android開發最佳實踐

1.注意對隱式Intent的運行時檢查保護

  1. 相似打開相機等隱式Intent,不必定可以在全部的Android設備上都正常運行.
    • 例如系統相機應用被關閉或者不存在相機應用,或者某些權限被關閉均可能致使拋出ActivityNotFoundException的異常.
    • 預防這個問題的最佳解決方案是在發出這個隱式Intent以前調用resolveActivity作檢查
  2. 代碼實例
    public class IntentCheckActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_intent_check);
        }
        public void openSBTest(View view) {
            // 跳轉到"傻逼"軟件
            Intent sbIntent = new Intent("android.media.action.IMAGE_GO_SB");
            if (sbIntent.resolveActivity(getPackageManager()) != null) {
                startActivity(sbIntent);
            } else {
                //會彈出這個提示
                Toast.makeText(this,"設備木有傻逼!",Toast.LENGTH_SHORT).show();
            }
        }
        public void openCameraTest(View view) {
            // 跳轉到系統照相機
            Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            if (cameraIntent.resolveActivity(getPackageManager()) != null) {
                startActivity(cameraIntent);
                //正常設備會進入相機並彈出提示
                Toast.makeText(this,"設備有相機!",Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this,"設備木有相機!",Toast.LENGTH_SHORT).show();
            }
        }
    }
    複製代碼

2.Android 6.0的權限

3.MD新控件的使用:Toolbar替代ActionBar,AppBarLayout,Navigation Drawer, DrawerLayout, NavigationView等

7.Android性能優化典範-第4季

1.網絡數據的緩存.okHttp,Picasso都支持網絡緩存

okHttp Picasso
MVP架構實現的Github客戶端(4-加入網絡緩存)

2.代碼混淆

2.1.AS中生成keystore.jks應用於APK打包

  • 1:生成keystore.jks


  • 2:查看.jks文件的SHA1安全碼
    在AS的Terminal中輸入:
    keytool -list -v -keystore C:\Users\Administrator\Desktop\key.jks
    keytool -list -v -keystore .jks文件詳細路徑
    回車後,輸入密鑰庫口令/就是.jks的密碼,輸入過程不可見,輸入完畢回車便可!

2.2.proguard-rules關鍵字及部分通配符含義

關鍵字 描述
keep 保留類和類中的成員,防止它們被混淆或移除
keepnames 保留類和類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除
keepclasseswithmembers 保留類和類中的成員,防止它們被混淆或移除,前提是指名的類中的成員必須存在,若是不存在則仍是會混淆
keepclasseswithmembernames 保留類和類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除,前提是指名的類中的成員必須存在,若是不存在則仍是會混淆
keepclassmembers 只保留類中的成員,防止它們被混淆或移除
keepclassmembernames 只保留類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除

通配符 描述
< field > 匹配類中的全部字段
< method > 匹配類中的全部方法
< init > 匹配類中的全部構造函數
* 1.*和字符串聯合使用,*表明任意長度的不包含包名分隔符(.)的字符串:
a.b.c.MainActivity: a.*.*.MainActivity能夠匹配;a.*就匹配不上;
2.*單獨使用,就能夠匹配全部東西
** 匹配任意長度字符串,包含包名分隔符(.)
a.b.**能夠匹配a.b包下全部內容,包括子包
*** 匹配任意參數類型.好比:
void set*(***)就能匹配任意傳入的參數類型;
*** get*()就能匹配任意返回值的類型
匹配任意長度的任意類型參數.好比:
void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)

  • keep 完整類名{*;}, 能夠對指定類進行徹底保留,不混淆類名,變量名,方法名.
    -keep public class a.b.c.TestItem{
        *;
    }
    複製代碼
  • 在App中,咱們會定義不少實體bean.每每涉及到bean實例和json字符串間互相轉換.部分json庫會經過反射調用bean的set和get方法.於是實體bean的set,get方法不能被混淆,或者說咱們本身寫的方法,若是會被第三方庫或其餘地方經過反射調用,則指定方法要keep避免混淆.
    -keep public class a.**.Bean.**{
        public void set*(***);
        public *** get*();
        # 對應獲取boolean類型屬性的方法
        public *** is*();
    }
    複製代碼
  • 咱們本身寫的使用了反射功能的類,必須keep
    #保留單個包含反射代碼的類
    -keep public class a.b.c.ReflectUtils{
        *;
    }
    #保留全部包含反射代碼的類,好比全部涉及反射代碼的類都在a.b.reflectpackage包及其子包下
    -keep class a.b.reflectpackage.**{
        *;
    } 
    複製代碼
  • 若是咱們要保留繼承了指定類的子類,或者實現了指定接口的類
    -keep class * extends a.b.c.Parent{*;}
    -keep class * implements a.b.c.OneInterface{*;}
    複製代碼

2.3.proguard-rules.pro通用模板

#####################基本指令##############################################
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
-renamesourcefileattribute SourceFile
#代碼混淆壓縮比,在0~7之間,默認爲5,通常不須要改
-optimizationpasses 5
#混淆時不使用大小寫混合,混淆後的類名爲小寫
-dontusemixedcaseclassnames
#指定不去忽略非公共的庫的類
-dontskipnonpubliclibraryclasses
#指定不去忽略非公共的庫的類的成員
-dontskipnonpubliclibraryclassmembers
#不作預校驗,preverify是proguard的4個步驟之一
#Android不須要preverify,去掉這一步可加快混淆速度
-dontpreverify
#有了verbose這句話,混淆後就會生成映射文件
#包含有類名->混淆後類名的映射關係
-verbose
#而後使用printmapping指定映射文件的名稱
-printmapping mapping.txt
#指定混淆時採用的算法,後面的參數是一個過濾器,這個過濾器是谷歌推薦的算法,通常不改變
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保護代碼中的Annotation不被混淆,這在JSON實體映射時很是重要(保留註解參數)
-keepattributes *Annotation*
#避免混淆泛型,這在JSON實體映射時很是重要
-keepattributes Signature
#拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable

#忽略全部警告
-ignorewarnings

###################須要保留的東西########################################

#保留反射的方法和類不被混淆================================================
#手動啓用support keep註解
#http://tools.android.com/tech-docs/support-annotations

-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

#==========================================================================================

#保留全部的本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
#保留了繼承自Activity、Application這些類的子類
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Fragment
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class * extends com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}

#保留在Activity中的方法參數是view的方法,從而咱們在layout裏面便攜onClick就不會受影響
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}
#枚舉類不能被混淆
-keepclassmembers enum *{
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
#保留自定義控件不被混淆
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    void set*(***);
    *** get*();
}
#保留Parcelable序列化的類不被混淆
-keep class * implements android.os.Paracelable{
    public static final android.os.Paracelable$Creator *;
}
#保留Serializable序列化的類的以下成員不被混淆
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    !private <fields>;
    !private <methods>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
#對於R(資源)下的全部類及其方法,都不能被混淆
-keep class **.R$*{
    *;
}
#R文件中的全部記錄資源id的靜態字段
-keepclassmembers class **.R$* {
    public static <fields>;
}
#對於帶有回調函數onXXEvent的,不能被混淆
-keepclassmembers class * {
    void *(**On*Event);
}

#============================針對app的量身定製=============================================

# webView處理,項目中沒有使用到webView忽略便可
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
    public void *(android.webkit.webView, java.lang.String);
}
複製代碼

2.4.混淆jar包
郭霖大神博客有介紹,本身沒試過

2.5.幾條實用的Proguard rules
在上面提供的通用模板上繼續添加下面幾行:

-repackageclasses com
-obfuscationdictionary dict.txt
-classobfuscationdictionary dict.txt
-packageobfuscationdictionary dict.txt

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}
複製代碼
  1. repackageclasses:除了keep的類,會把咱們本身寫的全部類以及所使用到的各類第三方庫代碼通通移動到咱們指定的單個包下.
    • 好比一些比較敏感的被keep的類在包a.b.min下,咱們可使用 -repackageclasses a.b.min,這樣就有成千上萬的被混淆的類和未被混淆的敏感的類在a.b.min下面,正常人根本就找不到關鍵類.尤爲是keep的類也只是保留關鍵方法,名字也被混淆過.
  2. -obfuscationdictionary,-classobfuscationdictionary和-packageobfuscationdictionary分別指定變量/方法名,類名,包名混淆後的字符串集.
    • 默認咱們的代碼命名會被混淆成字母組合,使用這些配置能夠用亂碼或中文內容進行命名.中文命名能夠破壞部分反編譯軟件的正常工做,亂碼則極大加大了查看代碼的難度.
    • dict.txt:須要放到和app模塊的proguard-rules.pro同級目錄.dict.txt具體內容能夠本身寫,參考開源項目:一種生成閱讀極其困難的proguard字典的算法
  3. -assumenosideeffects class android.util.Log是在編譯成 APK 以前把日誌代碼所有刪掉.

2.6.字符串硬編碼

  1. 對於反編譯者來講,最簡單的入手點就是字符串搜索.硬編碼留在代碼裏的字符串值都會在反編譯過程當中被原樣恢復,不要使用硬編碼.
  2. 若是必定要使用硬編碼
    1. 新建1個存儲硬編碼的常量類,靜態存放字符串常量,即便找到了常量類,反編譯者很難搜索到哪裏用了這些字符串.
    2. 常量類中的靜態常量字符串,用名稱做爲真正內容,而值用難以理解的編碼表示.
      //1:新建常量類,用於存放字符串常量
      public class HardStrings {
          //2:名稱是真正內容,值是難以理解的編碼.
          //這樣即便是必須保存的Log,被反編譯者看到的也只是難以理解的值,搞不清意義
          public static final String MaxMemory = "001";
          public static final String M = "002";
          public static final String MemoryClass = "003";
          public static final String LargeMemoryClass = "004";
          public static final String 系統總內存 = "005";
          public static final String 系統剩餘內存 = "006";
          public static final String 系統是否處於低內存運行 = "007";
          public static final String 系統剩餘內存低於 = "008";
          public static final String M時爲低內存運行 = "009";
      }
      複製代碼

2.7.res資源混淆及多渠道打包
簡單講,使用騰訊的2個gradle插件來實現res資源混淆及多渠道打包.
res資源混淆:AndResGuard
多渠道打包:VasDolly
多渠道打包原理+VasDolly和其餘多渠道打包方案對比

具體流程:
AndResGuard使用了chaychan的方法,單首創建gradle文件

  1. 項目根目錄下build.gradle中,添加插件的依賴,具體以下
    buildscript {
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.4'
    
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
            //添加AndResGuard
            classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.12'
            //添加VasDolly
            classpath 'com.leon.channel:plugin:2.0.1'
        }
    }
    複製代碼
  2. 在app目錄下單首創建gradle文件and_res_guard.gradle.內容以下
    apply plugin: 'AndResGuard'
    
    andResGuard {
        mappingFile = null
        use7zip = true
        useSign = true
        keepRoot = false
        compressFilePattern = [
                "*.png",
                "*.jpg",
                "*.jpeg",
                "*.gif",
                "resources.arsc"
        ]
        whiteList = [
    //            // your icon
    //            "R.drawable.icon",
    //            // for fabric
    //            "R.string.com.crashlytics.*",
    //            // for umeng update
    //            "R.string.tb_*",
    //            "R.layout.tb_*",
    //            "R.drawable.tb_*",
    //            "R.drawable.u1*",
    //            "R.drawable.u2*",
    //            "R.color.tb_*",
    //            // umeng share for sina
    //            "R.drawable.sina*",
    //            // for google-services.json
    //            "R.string.google_app_id",
    //            "R.string.gcm_defaultSenderId",
    //            "R.string.default_web_client_id",
    //            "R.string.ga_trackingId",
    //            "R.string.firebase_database_url",
    //            "R.string.google_api_key",
    //            "R.string.google_crash_reporting_api_key",
    //
    //            //友盟
    //            "R.string.umeng*",
    //            "R.string.UM*",
    //            "R.layout.umeng*",
    //            "R.drawable.umeng*",
    //            "R.id.umeng*",
    //            "R.anim.umeng*",
    //            "R.color.umeng*",
    //            "R.style.*UM*",
    //            "R.style.umeng*",
    //
    //            //融雲
    //            "R.drawable.u*",
    //            "R.drawable.rc_*",
    //            "R.string.rc_*",
    //            "R.layout.rc_*",
    //            "R.color.rc_*",
    //            "R.id.rc_*",
    //            "R.style.rc_*",
    //            "R.dimen.rc_*",
    //            "R.array.rc_*"
        ]
    
        sevenzip {
            artifact = 'com.tencent.mm:SevenZip:1.2.12'
            //path = "/usr/local/bin/7za"
        }
    }
    複製代碼
  3. 模塊app下的build.gradle文件添加依賴,具體以下
    apply plugin: 'com.android.application'
    //引入剛剛建立的and_res_guard.gradle
    apply from: 'and_res_guard.gradle'
    //依賴VasDolly
    apply plugin: 'channel'
    
    channel{
        //指定渠道文件
        channelFile = file("channel.txt")
        //多渠道包的輸出目錄,默認爲new File(project.buildDir,"channel")
        baseOutputDir = new File(project.buildDir,"channel")
        //多渠道包的命名規則,默認爲:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
        apkNameFormat ='${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
        //快速模式:生成渠道包時不進行校驗(速度能夠提高10倍以上,默認爲false)
        isFastMode = true
        //buildTime的時間格式,默認格式:yyyyMMdd-HHmmss
        buildTimeDateFormat = 'yyyyMMdd-HH:mm:ss'
        //低內存模式(僅針對V2簽名,默認爲false):只把簽名塊、中央目錄和EOCD讀取到內存,不把最大頭的內容塊讀取到內存,在手機上合成APK時,可使用該模式
        lowMemory = false
    }
    rebuildChannel {
        //指定渠道文件
        channelFile = file("channel.txt")
    //    baseDebugApk = new File(project.projectDir, "app-release_7zip_aligned_signed.apk")
        baseReleaseApk = new File(project.projectDir, "app-release_7zip_aligned_signed.apk")
        //默認爲new File(project.buildDir, "rebuildChannel/debug")
    //    debugOutputDir = new File(project.buildDir, "rebuildChannel/debug")
        //默認爲new File(project.buildDir, "rebuildChannel/release")
        releaseOutputDir = new File(project.buildDir, "rebuildChannel/release")
        //快速模式:生成渠道包時不進行校驗(速度能夠提高10倍以上,默認爲false)
        isFastMode = false
        //低內存模式(僅針對V2簽名,默認爲false):只把簽名塊、中央目錄和EOCD讀取到內存,不把最大頭的內容塊讀取到內存,在手機上合成APK時,可使用該模式
        lowMemory = false
    }
    
    android {
        signingConfigs {
            tcl {
                keyAlias 'qinghailongxin'
                keyPassword 'huanhailiuxin'
                storeFile file('C:/Users/Administrator/Desktop/key.jks')
                storePassword 'huanhailiuxin'
            }
        }
        compileSdkVersion 28
        defaultConfig {
            applicationId "com.example.administrator.proguardapp"
            minSdkVersion 15
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            vectorDrawables.useSupportLibrary = true
        }
        buildTypes {
            release {
                minifyEnabled true
                shrinkResources true
                zipAlignEnabled true
                pseudoLocalesEnabled true
                proguardFiles 'proguard-rules.pro'
                signingConfig signingConfigs.tcl
            }
            debug {
                signingConfig signingConfigs.tcl
                minifyEnabled true
                pseudoLocalesEnabled true
                zipAlignEnabled true
            }
        }
    }
    
    dependencies {
        implementation fileTree(include: ['*.jar'], dir: 'libs')
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        implementation 'com.android.support:design:28.0.0'
        implementation 'com.android.support:support-vector-drawable:28.0.0'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
        implementation files('libs/litepal-2.0.0.jar')
        //依賴VasDolly
        api 'com.leon.channel:helper:2.0.1'
    }
    複製代碼
  4. 首先使用AndResGuard實現資源混淆,再使用VasDolly實現多渠道打包
    1. 在Gradle界面中,找到app模塊下andresguard的task.

      • 若是想打debug包,則執行resguardDebug指令;
      • 若是想打release包,則執行resguardRelease指令.
      • 此處咱們雙擊執行resguardRelease指令,在app目錄下的/build/output/apk/release/AndResGuard_{apk_name}/ 文件夾中找到混淆後的Apk,其中app-release_aligned_signed.apk爲進行混淆並簽名過的apk.
      • 咱們查看app-release_aligned_signed.apk,res文件夾改名爲r,裏面的目錄名稱以及xml文件已經被混淆.
    2. 將app-release_aligned_signed.apk放到app模塊下,在Gradle界面中,找到app模塊下channel的task,執行reBuildChannel指令.

      • 雙擊執行reBuildChannel指令,幾秒鐘就生成了20個經過app-release_aligned_signed.apk的多渠道apk.
      • 經過helper類庫中的ChannelReaderUtil類讀取渠道信息
        String channel = ChannelReaderUtil.getChannel(getApplicationContext());
        複製代碼

3.APK瘦身

4.更高效的數據序列化:只是看看從沒用過,Protocal Buffers,Nano-Proto-Buffers,FlatBuffers

5.數據呈現的順序以及結構會對序列化以後的空間產生不小的影響

改變數據結構對內存及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壓縮,須要使用自定義攔截器
        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<Person> l = new ArrayList<Person>(); 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<String> names = new ArrayList<String>(); List<String> genders = new ArrayList<String>(); List<Integer> ages = new ArrayList<Integer>(); 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<100;i++){ //1:原始數據結構 //Gzip壓縮 long start = System.currentTimeMillis(); String gzipStr = ZipUtils.compressForGzip(jsonDataOri); long end = System.currentTimeMillis(); System.out.println("原始數據結構 Gzip壓縮耗時 cost time---->" + (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<Person> l = new ArrayList<Person>();
              for(repetitionRatio = 0.000F; repetitionRatio < 0.500F; repetitionRatio+=0.005F){
                  int reportIndex = (int) (6000 * (1-repetitionRatio));
                  for(int i = 0;i<reportIndex;i++){
                      Person p = new Person();
                      p.age = i;
                      p.gender = "gender"+i;
                      p.name = "name"+i;
                      l.add(p);
                  }
                  if(repetitionRatio > 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<String> names = new ArrayList<String>();
                  List<String> genders = new ArrayList<String>();
                  List<Integer> ages = new ArrayList<Integer>();
                  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<Person> items;
      
              public List<Person> getItems() {
                  return items;
              }
      
              public void setItems(List<Person> items) {
                  this.items = items;
              }
          }
          public class PersonAttrList implements Serializable{
              public List<String> names;
              public List<String> genders;
              public List<Integer> ages;
      
              public List<String> getNames() {
                  return names;
              }
      
              public void setNames(List<String> names) {
                  this.names = names;
              }
      
              public List<String> getGenders() {
                  return genders;
              }
      
              public void setGenders(List<String> genders) {
                  this.genders = genders;
              }
      
              public List<Integer> getAges() {
                  return ages;
              }
      
              public void setAges(List<Integer> ages) {
                  this.ages = ages;
              }
          }
      }
      
      首先看當單個對象屬性重複率超過100%的狀況下打印結果:
      
      List<Person> l = new ArrayList<Person>();
              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);
                  }
      }
      原始數據結構 壓縮前json數據長度 ---->273011	//由於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數據長度 ---->3453191
      變種數據結構 壓縮前json數據長度 ---->1869356
      原始數據結構 Gzip壓縮後json數據長度 ---->476356
      變種數據結構 Gzip壓縮後json數據長度 ---->291550
      重複率爲 0.052631572 壓縮後:原始結構長度:變種數據結構=1.6338742
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->3766491
      變種數據結構 壓縮前json數據長度 ---->2038680
      原始數據結構 Gzip壓縮後json數據長度 ---->518371
      變種數據結構 Gzip壓縮後json數據長度 ---->317122
      重複率爲 0.058201052 壓縮後:原始結構長度:變種數據結構=1.6346107
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->4079701
      變種數據結構 壓縮前json數據長度 ---->2207914
      原始數據結構 Gzip壓縮後json數據長度 ---->560526
      變種數據結構 Gzip壓縮後json數據長度 ---->344023
      重複率爲 0.06382978 壓縮後:原始結構長度:變種數據結構=1.629327
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->4392821
      變種數據結構 壓縮前json數據長度 ---->2377058
      原始數據結構 Gzip壓縮後json數據長度 ---->602208
      變種數據結構 Gzip壓縮後json數據長度 ---->365983
      重複率爲 0.06951871 壓縮後:原始結構長度:變種數據結構=1.6454535
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->4705851
      變種數據結構 壓縮前json數據長度 ---->2546112
      原始數據結構 Gzip壓縮後json數據長度 ---->643532
      變種數據結構 Gzip壓縮後json數據長度 ---->391465
      重複率爲 0.07526881 壓縮後:原始結構長度:變種數據結構=1.6439068
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->5018791
      變種數據結構 壓縮前json數據長度 ---->2715076
      原始數據結構 Gzip壓縮後json數據長度 ---->684775
      變種數據結構 Gzip壓縮後json數據長度 ---->415902
      重複率爲 0.08108108 壓縮後:原始結構長度:變種數據結構=1.6464816
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->5331691
      變種數據結構 壓縮前json數據長度 ---->2883976
      原始數據結構 Gzip壓縮後json數據長度 ---->725952
      變種數據結構 Gzip壓縮後json數據長度 ---->438987
      重複率爲 0.086956516 壓縮後:原始結構長度:變種數據結構=1.6536982
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->5644501
      變種數據結構 壓縮前json數據長度 ---->3052786
      原始數據結構 Gzip壓縮後json數據長度 ---->767578
      變種數據結構 Gzip壓縮後json數據長度 ---->464169
      重複率爲 0.09289617 壓縮後:原始結構長度:變種數據結構=1.6536607
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->5957221
      變種數據結構 壓縮前json數據長度 ---->3221506
      原始數據結構 Gzip壓縮後json數據長度 ---->808616
      變種數據結構 Gzip壓縮後json數據長度 ---->488167
      重複率爲 0.09890111 壓縮後:原始結構長度:變種數據結構=1.6564331
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->6269851
      變種數據結構 壓縮前json數據長度 ---->3390136
      原始數據結構 Gzip壓縮後json數據長度 ---->848776
      變種數據結構 Gzip壓縮後json數據長度 ---->511159
      重複率爲 0.104972385 壓縮後:原始結構長度:變種數據結構=1.6604931
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->6582391
      變種數據結構 壓縮前json數據長度 ---->3558676
      原始數據結構 Gzip壓縮後json數據長度 ---->889184
      變種數據結構 Gzip壓縮後json數據長度 ---->536695
      重複率爲 0.11111113 壓縮後:原始結構長度:變種數據結構=1.6567771
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->6894841
      變種數據結構 壓縮前json數據長度 ---->3727126
      原始數據結構 Gzip壓縮後json數據長度 ---->928982
      變種數據結構 Gzip壓縮後json數據長度 ---->557274
      重複率爲 0.11731845 壓縮後:原始結構長度:變種數據結構=1.6670111
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->7207201
      變種數據結構 壓縮前json數據長度 ---->3895486
      原始數據結構 Gzip壓縮後json數據長度 ---->968845
      變種數據結構 Gzip壓縮後json數據長度 ---->583064
      重複率爲 0.12359552 壓縮後:原始結構長度:變種數據結構=1.6616443
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->7519471
      變種數據結構 壓縮前json數據長度 ---->4063756
      原始數據結構 Gzip壓縮後json數據長度 ---->1013093
      變種數據結構 Gzip壓縮後json數據長度 ---->606056
      重複率爲 0.12994352 壓縮後:原始結構長度:變種數據結構=1.6716162
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->7831651
      變種數據結構 壓縮前json數據長度 ---->4231936
      原始數據結構 Gzip壓縮後json數據長度 ---->1057283
      變種數據結構 Gzip壓縮後json數據長度 ---->626963
      重複率爲 0.13636366 壓縮後:原始結構長度:變種數據結構=1.6863563
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->8143741
      變種數據結構 壓縮前json數據長度 ---->4400026
      原始數據結構 Gzip壓縮後json數據長度 ---->1101480
      變種數據結構 Gzip壓縮後json數據長度 ---->650165
      重複率爲 0.14285716 壓縮後:原始結構長度:變種數據結構=1.6941546
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->8455741
      變種數據結構 壓縮前json數據長度 ---->4568026
      原始數據結構 Gzip壓縮後json數據長度 ---->1145324
      變種數據結構 Gzip壓縮後json數據長度 ---->675800
      重複率爲 0.1494253 壓縮後:原始結構長度:變種數據結構=1.6947677
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->8767651
      變種數據結構 壓縮前json數據長度 ---->4735936
      原始數據結構 Gzip壓縮後json數據長度 ---->1189441
      變種數據結構 Gzip壓縮後json數據長度 ---->696474
      重複率爲 0.15606937 壓縮後:原始結構長度:變種數據結構=1.7078038
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->9079471
      變種數據結構 壓縮前json數據長度 ---->4903756
      原始數據結構 Gzip壓縮後json數據長度 ---->1233352
      變種數據結構 Gzip壓縮後json數據長度 ---->720694
      重複率爲 0.1627907 壓縮後:原始結構長度:變種數據結構=1.7113394
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->9391201
      變種數據結構 壓縮前json數據長度 ---->5071486
      原始數據結構 Gzip壓縮後json數據長度 ---->1277550
      變種數據結構 Gzip壓縮後json數據長度 ---->741108
      重複率爲 0.16959064 壓縮後:原始結構長度:變種數據結構=1.7238379
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->9702791
      變種數據結構 壓縮前json數據長度 ---->5239100
      原始數據結構 Gzip壓縮後json數據長度 ---->1321359
      變種數據結構 Gzip壓縮後json數據長度 ---->763320
      重複率爲 0.17647058 壓縮後:原始結構長度:變種數據結構=1.7310683
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->10014291
      變種數據結構 壓縮前json數據長度 ---->5406624
      原始數據結構 Gzip壓縮後json數據長度 ---->1365756
      變種數據結構 Gzip壓縮後json數據長度 ---->782468
      重複率爲 0.18343192 壓縮後:原始結構長度:變種數據結構=1.7454464
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->10325701
      變種數據結構 壓縮前json數據長度 ---->5574058
      原始數據結構 Gzip壓縮後json數據長度 ---->1409791
      變種數據結構 Gzip壓縮後json數據長度 ---->809521
      重複率爲 0.19047616 壓縮後:原始結構長度:變種數據結構=1.7415125
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->10637021
      變種數據結構 壓縮前json數據長度 ---->5741402
      原始數據結構 Gzip壓縮後json數據長度 ---->1453682
      變種數據結構 Gzip壓縮後json數據長度 ---->828981
      重複率爲 0.19760476 壓縮後:原始結構長度:變種數據結構=1.753577
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->10948308
      變種數據結構 壓縮前json數據長度 ---->5908713
      原始數據結構 Gzip壓縮後json數據長度 ---->1497843
      變種數據結構 Gzip壓縮後json數據長度 ---->852966
      重複率爲 0.20481923 壓縮後:原始結構長度:變種數據結構=1.7560407
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->11259595
      變種數據結構 壓縮前json數據長度 ---->6076024
      原始數據結構 Gzip壓縮後json數據長度 ---->1542039
      變種數據結構 Gzip壓縮後json數據長度 ---->872647
      重複率爲 0.21212116 壓縮後:原始結構長度:變種數據結構=1.7670822
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->11570882
      變種數據結構 壓縮前json數據長度 ---->6243335
      原始數據結構 Gzip壓縮後json數據長度 ---->1585781
      變種數據結構 Gzip壓縮後json數據長度 ---->891023
      重複率爲 0.21951213 壓縮後:原始結構長度:變種數據結構=1.7797307
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->11882169
      變種數據結構 壓縮前json數據長度 ---->6410646
      原始數據結構 Gzip壓縮後json數據長度 ---->1629443
      變種數據結構 Gzip壓縮後json數據長度 ---->915561
      重複率爲 0.2269938 壓縮後:原始結構長度:變種數據結構=1.7797209
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->12193456
      變種數據結構 壓縮前json數據長度 ---->6577957
      原始數據結構 Gzip壓縮後json數據長度 ---->1673135
      變種數據結構 Gzip壓縮後json數據長度 ---->937219
      重複率爲 0.23456782 壓縮後:原始結構長度:變種數據結構=1.7852124
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->12504743
      變種數據結構 壓縮前json數據長度 ---->6745268
      原始數據結構 Gzip壓縮後json數據長度 ---->1717525
      變種數據結構 Gzip壓縮後json數據長度 ---->956429
      重複率爲 0.24223594 壓縮後:原始結構長度:變種數據結構=1.7957684
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->12816030
      變種數據結構 壓縮前json數據長度 ---->6912579
      原始數據結構 Gzip壓縮後json數據長度 ---->1761849
      變種數據結構 Gzip壓縮後json數據長度 ---->976092
      重複率爲 0.24999991 壓縮後:原始結構長度:變種數據結構=1.805003
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->13127317
      變種數據結構 壓縮前json數據長度 ---->7079890
      原始數據結構 Gzip壓縮後json數據長度 ---->1806001
      變種數據結構 Gzip壓縮後json數據長度 ---->995442
      重複率爲 0.25786152 壓縮後:原始結構長度:變種數據結構=1.8142705
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->13438604
      變種數據結構 壓縮前json數據長度 ---->7247201
      原始數據結構 Gzip壓縮後json數據長度 ---->1850241
      變種數據結構 Gzip壓縮後json數據長度 ---->1014463
      重複率爲 0.26582268 壓縮後:原始結構長度:變種數據結構=1.8238624
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->13749891
      變種數據結構 壓縮前json數據長度 ---->7414512
      原始數據結構 Gzip壓縮後json數據長度 ---->1893946
      變種數據結構 Gzip壓縮後json數據長度 ---->1038690
      重複率爲 0.27388522 壓縮後:原始結構長度:變種數據結構=1.8233987
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->14061178
      變種數據結構 壓縮前json數據長度 ---->7581823
      原始數據結構 Gzip壓縮後json數據長度 ---->1938584
      變種數據結構 Gzip壓縮後json數據長度 ---->1064229
      重複率爲 0.28205115 壓縮後:原始結構長度:變種數據結構=1.8215854
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->14372465
      變種數據結構 壓縮前json數據長度 ---->7749134
      原始數據結構 Gzip壓縮後json數據長度 ---->1982416
      變種數據結構 Gzip壓縮後json數據長度 ---->1079948
      重複率爲 0.29032245 壓縮後:原始結構長度:變種數據結構=1.8356588
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->14683752
      變種數據結構 壓縮前json數據長度 ---->7916445
      原始數據結構 Gzip壓縮後json數據長度 ---->2026663
      變種數據結構 Gzip壓縮後json數據長度 ---->1102001
      重複率爲 0.29870114 壓縮後:原始結構長度:變種數據結構=1.8390754
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->14995039
      變種數據結構 壓縮前json數據長度 ---->8083756
      原始數據結構 Gzip壓縮後json數據長度 ---->2070714
      變種數據結構 Gzip壓縮後json數據長度 ---->1125712
      重複率爲 0.30718938 壓縮後:原始結構長度:變種數據結構=1.8394705
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->15306326
      變種數據結構 壓縮前json數據長度 ---->8251067
      原始數據結構 Gzip壓縮後json數據長度 ---->2114297
      變種數據結構 Gzip壓縮後json數據長度 ---->1145723
      重複率爲 0.3157893 壓縮後:原始結構長度:變種數據結構=1.8453823
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->15617613
      變種數據結構 壓縮前json數據長度 ---->8418378
      原始數據結構 Gzip壓縮後json數據長度 ---->2158166
      變種數據結構 Gzip壓縮後json數據長度 ---->1164141
      重複率爲 0.32450312 壓縮後:原始結構長度:變種數據結構=1.8538699
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->15928900
      變種數據結構 壓縮前json數據長度 ---->8585689
      原始數據結構 Gzip壓縮後json數據長度 ---->2201712
      變種數據結構 Gzip壓縮後json數據長度 ---->1189557
      重複率爲 0.33333313 壓縮後:原始結構長度:變種數據結構=1.8508672
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->16240187
      變種數據結構 壓縮前json數據長度 ---->8753000
      原始數據結構 Gzip壓縮後json數據長度 ---->2245653
      變種數據結構 Gzip壓縮後json數據長度 ---->1207825
      重複率爲 0.3422817 壓縮後:原始結構長度:變種數據結構=1.8592536
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->16551474
      變種數據結構 壓縮前json數據長度 ---->8920311
      原始數據結構 Gzip壓縮後json數據長度 ---->2289778
      變種數據結構 Gzip壓縮後json數據長度 ---->1228716
      重複率爲 0.35135114 壓縮後:原始結構長度:變種數據結構=1.8635535
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->16862761
      變種數據結構 壓縮前json數據長度 ---->9087622
      原始數據結構 Gzip壓縮後json數據長度 ---->2333883
      變種數據結構 Gzip壓縮後json數據長度 ---->1248197
      重複率爲 0.36054403 壓縮後:原始結構長度:變種數據結構=1.8698034
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->17174048
      變種數據結構 壓縮前json數據長度 ---->9254933
      原始數據結構 Gzip壓縮後json數據長度 ---->2377734
      變種數據結構 Gzip壓縮後json數據長度 ---->1263293
      重複率爲 0.3698628 壓縮後:原始結構長度:變種數據結構=1.8821714
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->17485335
      變種數據結構 壓縮前json數據長度 ---->9422244
      原始數據結構 Gzip壓縮後json數據長度 ---->2421204
      變種數據結構 Gzip壓縮後json數據長度 ---->1286647
      重複率爲 0.3793101 壓縮後:原始結構長度:變種數據結構=1.8817935
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->17796622
      變種數據結構 壓縮前json數據長度 ---->9589555
      原始數據結構 Gzip壓縮後json數據長度 ---->2464871
      變種數據結構 Gzip壓縮後json數據長度 ---->1307479
      重複率爲 0.38888866 壓縮後:原始結構長度:變種數據結構=1.8852088
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->18107909
      變種數據結構 壓縮前json數據長度 ---->9756866
      原始數據結構 Gzip壓縮後json數據長度 ---->2508873
      變種數據結構 Gzip壓縮後json數據長度 ---->1327997
      重複率爲 0.39860114 壓縮後:原始結構長度:變種數據結構=1.8892158
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->18419196
      變種數據結構 壓縮前json數據長度 ---->9924177
      原始數據結構 Gzip壓縮後json數據長度 ---->2552954
      變種數據結構 Gzip壓縮後json數據長度 ---->1342020
      重複率爲 0.40845042 壓縮後:原始結構長度:變種數據結構=1.9023218
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->18730483
      變種數據結構 壓縮前json數據長度 ---->10091488
      原始數據結構 Gzip壓縮後json數據長度 ---->2596616
      變種數據結構 Gzip壓縮後json數據長度 ---->1369092
      重複率爲 0.41843942 壓縮後:原始結構長度:變種數據結構=1.8965971
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->19041770
      變種數據結構 壓縮前json數據長度 ---->10258799
      原始數據結構 Gzip壓縮後json數據長度 ---->2640984
      變種數據結構 Gzip壓縮後json數據長度 ---->1383626
      重複率爲 0.42857113 壓縮後:原始結構長度:變種數據結構=1.9087412
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->19353057
      變種數據結構 壓縮前json數據長度 ---->10426110
      原始數據結構 Gzip壓縮後json數據長度 ---->2685199
      變種數據結構 Gzip壓縮後json數據長度 ---->1402782
      重複率爲 0.4388486 壓縮後:原始結構長度:變種數據結構=1.9141955
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->19664344
      變種數據結構 壓縮前json數據長度 ---->10593421
      原始數據結構 Gzip壓縮後json數據長度 ---->2729710
      變種數據結構 Gzip壓縮後json數據長度 ---->1418750
      重複率爲 0.44927505 壓縮後:原始結構長度:變種數據結構=1.9240247
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->19975631
      變種數據結構 壓縮前json數據長度 ---->10760732
      原始數據結構 Gzip壓縮後json數據長度 ---->2773735
      變種數據結構 Gzip壓縮後json數據長度 ---->1435122
      重複率爲 0.45985368 壓縮後:原始結構長度:變種數據結構=1.932752
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->20286918
      變種數據結構 壓縮前json數據長度 ---->10928043
      原始數據結構 Gzip壓縮後json數據長度 ---->2818175
      變種數據結構 Gzip壓縮後json數據長度 ---->1458645
      重複率爲 0.47058788 壓縮後:原始結構長度:變種數據結構=1.93205
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->20598205
      變種數據結構 壓縮前json數據長度 ---->11095354
      原始數據結構 Gzip壓縮後json數據長度 ---->2862715
      變種數據結構 Gzip壓縮後json數據長度 ---->1473688
      重複率爲 0.4814811 壓縮後:原始結構長度:變種數據結構=1.9425516
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->20909492
      變種數據結構 壓縮前json數據長度 ---->11262665
      原始數據結構 Gzip壓縮後json數據長度 ---->2906140
      變種數據結構 Gzip壓縮後json數據長度 ---->1497577
      重複率爲 0.49253693 壓縮後:原始結構長度:變種數據結構=1.9405613
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->21220779
      變種數據結構 壓縮前json數據長度 ---->11429976
      原始數據結構 Gzip壓縮後json數據長度 ---->2951053
      變種數據結構 Gzip壓縮後json數據長度 ---->1513485
      重複率爲 0.50375897 壓縮後:原始結構長度:變種數據結構=1.9498396
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->21532066
      變種數據結構 壓縮前json數據長度 ---->11597287
      原始數據結構 Gzip壓縮後json數據長度 ---->2995263
      變種數據結構 Gzip壓縮後json數據長度 ---->1528176
      重複率爲 0.5151511 壓縮後:原始結構長度:變種數據結構=1.9600248
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->21843353
      變種數據結構 壓縮前json數據長度 ---->11764598
      原始數據結構 Gzip壓縮後json數據長度 ---->3039623
      變種數據結構 Gzip壓縮後json數據長度 ---->1546990
      重複率爲 0.5267171 壓縮後:原始結構長度:變種數據結構=1.9648627
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->22154640
      變種數據結構 壓縮前json數據長度 ---->11931909
      原始數據結構 Gzip壓縮後json數據長度 ---->3083971
      變種數據結構 Gzip壓縮後json數據長度 ---->1563906
      重複率爲 0.5384611 壓縮後:原始結構長度:變種數據結構=1.971967
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->22465927
      變種數據結構 壓縮前json數據長度 ---->12099220
      原始數據結構 Gzip壓縮後json數據長度 ---->3128112
      變種數據結構 Gzip壓縮後json數據長度 ---->1580792
      重複率爲 0.55038714 壓縮後:原始結構長度:變種數據結構=1.9788258
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->22777214
      變種數據結構 壓縮前json數據長度 ---->12266531
      原始數據結構 Gzip壓縮後json數據長度 ---->3171693
      變種數據結構 Gzip壓縮後json數據長度 ---->1600344
      重複率爲 0.5624995 壓縮後:原始結構長度:變種數據結構=1.981882
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->23088501
      變種數據結構 壓縮前json數據長度 ---->12433842
      原始數據結構 Gzip壓縮後json數據長度 ---->3215617
      變種數據結構 Gzip壓縮後json數據長度 ---->1618740
      重複率爲 0.57480264 壓縮後:原始結構長度:變種數據結構=1.9864938
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->23399788
      變種數據結構 壓縮前json數據長度 ---->12601153
      原始數據結構 Gzip壓縮後json數據長度 ---->3259832
      變種數據結構 Gzip壓縮後json數據長度 ---->1637726
      重複率爲 0.5873011 壓縮後:原始結構長度:變種數據結構=1.9904624
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->23711075
      變種數據結構 壓縮前json數據長度 ---->12768464
      原始數據結構 Gzip壓縮後json數據長度 ---->3304008
      變種數據結構 Gzip壓縮後json數據長度 ---->1652686
      重複率爲 0.5999994 壓縮後:原始結構長度:變種數據結構=1.9991747
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->24022362
      變種數據結構 壓縮前json數據長度 ---->12935775
      原始數據結構 Gzip壓縮後json數據長度 ---->3347657
      變種數據結構 Gzip壓縮後json數據長度 ---->1670445
      重複率爲 0.61290264 壓縮後:原始結構長度:變種數據結構=2.004051
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->24333649
      變種數據結構 壓縮前json數據長度 ---->13103086
      原始數據結構 Gzip壓縮後json數據長度 ---->3391716
      變種數據結構 Gzip壓縮後json數據長度 ---->1683890
      重複率爲 0.62601566 壓縮後:原始結構長度:變種數據結構=2.0142148
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->24644936
      變種數據結構 壓縮前json數據長度 ---->13270397
      原始數據結構 Gzip壓縮後json數據長度 ---->3436086
      變種數據結構 Gzip壓縮後json數據長度 ---->1704452
      重複率爲 0.6393436 壓縮後:原始結構長度:變種數據結構=2.0159476
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->24956223
      變種數據結構 壓縮前json數據長度 ---->13437708
      原始數據結構 Gzip壓縮後json數據長度 ---->3480064
      變種數據結構 Gzip壓縮後json數據長度 ---->1719727
      重複率爲 0.65289193 壓縮後:原始結構長度:變種數據結構=2.0236142
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->25267510
      變種數據結構 壓縮前json數據長度 ---->13605019
      原始數據結構 Gzip壓縮後json數據長度 ---->3524494
      變種數據結構 Gzip壓縮後json數據長度 ---->1735590
      重複率爲 0.666666 壓縮後:原始結構長度:變種數據結構=2.030718
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->25578797
      變種數據結構 壓縮前json數據長度 ---->13772330
      原始數據結構 Gzip壓縮後json數據長度 ---->3569109
      變種數據結構 Gzip壓縮後json數據長度 ---->1757409
      重複率爲 0.6806716 壓縮後:原始結構長度:變種數據結構=2.0308926
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->25890084
      變種數據結構 壓縮前json數據長度 ---->13939641
      原始數據結構 Gzip壓縮後json數據長度 ---->3613919
      變種數據結構 Gzip壓縮後json數據長度 ---->1770126
      重複率爲 0.6949145 壓縮後:原始結構長度:變種數據結構=2.041617
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->26201371
      變種數據結構 壓縮前json數據長度 ---->14106952
      原始數據結構 Gzip壓縮後json數據長度 ---->3658034
      變種數據結構 Gzip壓縮後json數據長度 ---->1787002
      重複率爲 0.70940095 壓縮後:原始結構長度:變種數據結構=2.0470228
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->26512658
      變種數據結構 壓縮前json數據長度 ---->14274263
      原始數據結構 Gzip壓縮後json數據長度 ---->3702835
      變種數據結構 Gzip壓縮後json數據長度 ---->1799515
      重複率爲 0.7241371 壓縮後:原始結構長度:變種數據結構=2.057685
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->26823945
      變種數據結構 壓縮前json數據長度 ---->14441574
      原始數據結構 Gzip壓縮後json數據長度 ---->3746980
      變種數據結構 Gzip壓縮後json數據長度 ---->1818417
      重複率爲 0.7391296 壓縮後:原始結構長度:變種數據結構=2.0605724
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->27135232
      變種數據結構 壓縮前json數據長度 ---->14608885
      原始數據結構 Gzip壓縮後json數據長度 ---->3790555
      變種數據結構 Gzip壓縮後json數據長度 ---->1836003
      重複率爲 0.7543851 壓縮後:原始結構長度:變種數據結構=2.064569
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->27446519
      變種數據結構 壓縮前json數據長度 ---->14776196
      原始數據結構 Gzip壓縮後json數據長度 ---->3834464
      變種數據結構 Gzip壓縮後json數據長度 ---->1851563
      重複率爲 0.76991063 壓縮後:原始結構長度:變種數據結構=2.0709336
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->27757806
      變種數據結構 壓縮前json數據長度 ---->14943507
      原始數據結構 Gzip壓縮後json數據長度 ---->3879072
      變種數據結構 Gzip壓縮後json數據長度 ---->1873192
      重複率爲 0.7857134 壓縮後:原始結構長度:變種數據結構=2.0708354
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->28069093
      變種數據結構 壓縮前json數據長度 ---->15110818
      原始數據結構 Gzip壓縮後json數據長度 ---->3923316
      變種數據結構 Gzip壓縮後json數據長度 ---->1894024
      重複率爲 0.80180085 壓縮後:原始結構長度:變種數據結構=2.0714183
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->28380380
      變種數據結構 壓縮前json數據長度 ---->15278129
      原始數據結構 Gzip壓縮後json數據長度 ---->3967482
      變種數據結構 Gzip壓縮後json數據長度 ---->1916387
      重複率爲 0.81818086 壓縮後:原始結構長度:變種數據結構=2.0702927
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->28691667
      變種數據結構 壓縮前json數據長度 ---->15445440
      原始數據結構 Gzip壓縮後json數據長度 ---->4011094
      變種數據結構 Gzip壓縮後json數據長度 ---->1933486
      重複率爲 0.8348614 壓縮後:原始結構長度:變種數據結構=2.07454
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->29002954
      變種數據結構 壓縮前json數據長度 ---->15612751
      原始數據結構 Gzip壓縮後json數據長度 ---->4055289
      變種數據結構 Gzip壓縮後json數據長度 ---->1953997
      重複率爲 0.8518508 壓縮後:原始結構長度:變種數據結構=2.0753813
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->29314241
      變種數據結構 壓縮前json數據長度 ---->15780062
      原始數據結構 Gzip壓縮後json數據長度 ---->4099592
      變種數據結構 Gzip壓縮後json數據長度 ---->1974066
      重複率爲 0.8691578 壓縮後:原始結構長度:變種數據結構=2.076725
      ===================================================
      原始數據結構 壓縮前json數據長度 ---->29625528
      變種數據結構 壓縮前json數據長度 ---->15947373
      原始數據結構 Gzip壓縮後json數據長度 ---->4143573
      變種數據結構 Gzip壓縮後json數據長度 ---->1987771
      重複率爲 0.88679135 壓縮後:原始結構長度:變種數據結構=2.0845323
      ===================================================
      原始數據結構 壓縮前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
      
      複製代碼

8.Android性能優化典範-第5季

多線程大部份內容源自凱哥的課程,我的以爲比優化典範寫得清晰得多

1.線程

  1. 線程就是代碼線性執行,執行完畢就結束的一條線.UI線程不會結束是由於其初始化完畢後會執行死循環,因此永遠不會執行完畢.

  2. 如何簡單建立新線程:

    //1:直接建立Thread,執行其start方法
    Thread t1 = new Thread(){
        @Override
        public void run() {
            System.out.println("Thread:run");
        }
    };
    t1.start();
    //2:使用Runnable實例做爲參數建立Thread,執行start
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Runnable:run");
        }
    };
    Thread t2 = new Thread(runnable);
    t2.start();
    複製代碼
    • 兩種方式建立新線程性能無差異,使用Runnable實例適用於但願Runnable複用的情形
    • 經常使用的建立線程池2種方式
      1. Executors.newCachedThreadPool():通常狀況下使用newCachedThreadPool便可.
      2. Executors.newFixedThreadPool(int number):短時批量處理/好比要並行處理多張圖片,能夠直接建立包含圖片精確數量的線程的線程池並行處理.
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable:run()");
            }
        };
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(runnable);
        executorService.execute(runnable);
        executorService.execute(runnable);
        executorService.shutdown();
        //好比有40張圖片要同時處理
        //建立包含40個線程的線程池,每一個線程處理一張圖片,處理完畢後shutdown
        ExecutorService service = Executors.newFixedThreadPool(40);
        for(Bitmap item:bitmaps){
            //好比runnable就是處理單張圖片的
            service.execute(runnable);
        }
        service.shutdown();
        複製代碼
      3. 《阿里巴巴Java開發手冊》規定:
        • 線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方式.這樣 的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險
        • 看Android中Executors源碼.Executors.newCachedThreadPool/newScheduledThreadPool容許的建立線程數量爲 Integer.MAX_VALUE,可能會建立大量的線程,從而致使 OOM.而newFixedThreadPool,newSingleThreadExecutor不會存在這種風險.
      4. 如何正確建立ThreadPoolExecutor:有點麻煩,晚點詳述
      5. ExecutorService的shutdown和shutdownNow
        1. shutdown:在調用shutdown以前ExecutorService中已經啓動的線程,在調用shutdown後,線程若是執行未結束會繼續執行完畢並結束,但不會再啓動新的線程執行新任務.
        2. shutdownNow:首先中止啓動新的線程執行新任務;並嘗試結束全部正在執行的線程,正在執行的線程可能被終止也可能會繼續執行完成.
  3. 如何正確建立ThreadPoolExecutor
    3.1:ThreadPoolExecutor構造參數

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 複製代碼
    1. int corePoolSize:該線程池中核心線程最大數量.默認狀況下,即便核心線程處於空閒狀態也不會被銷燬.除非經過allowCoreThreadTimeOut(true),則核心線程在空閒時間達到keepAliveTime時會被銷燬
    2. int maximumPoolSize:該線程池中線程最大數量
    3. long keepAliveTime:該線程池中非核心線程被銷燬前最大空閒時間,時間單位由unit決定.默認狀況下核心線程即便空閒也不會被銷燬,在調用allowCoreThreadTimeOut(true)後,該銷燬時間設置也適用於核心線程
    4. TimeUnit unit:keepAliveTime/被銷燬前最大空閒時間的單位
    5. BlockingQueue workQueue:該線程池中的任務隊列.維護着等待被執行的Runnable對象.BlockingQueue有幾種類型,下面會詳述
    6. ThreadFactory threadFactory:建立新線程的工廠.通常狀況使用Executors.defaultThreadFactory()便可.固然也能夠自定義.
    7. RejectedExecutionHandler handler:拒絕策略.當須要建立的線程數量達到maximumPoolSize而且等待執行的Runnable數量超過了任務隊列的容量,該如何處理.

    3.2:當1個任務被放進線程池,ThreadPoolExecutor具體執行策略以下:

    1. 若是線程數量沒有達到corePoolSize,有核心線程空閒則核心線程直接執行,沒有空閒則直接新建核心線程執行任務;
    2. 若是線程數量已經達到corePoolSize,且核心線程無空閒,則將任務添加到等待隊列;
    3. 若是等待隊列已滿,則新建非核心線程執行該任務;
    4. 若是等待隊列已滿且總線程數量已達到maximumPoolSize,則會交由RejectedExecutionHandler handler處理.

    3.3:阻塞隊列/BlockingQueue workQueue

    1. BlockingQueue有以下幾種:SynchronousQueue/LinkedBlockingQueue/LinkedTransferQueue/ArrayBlockingQueue/PriorityBlockingQueue/DelayQueue.
    2. SynchronousQueue:SynchronousQueue的容量是0,不存儲任何Runnable實例.新任務到來會直接嘗試交給線程執行,如全部線程都在忙就建立新線程執行該任務.
    3. LinkedBlockingQueue:默認狀況下沒有容量限制的隊列.
    4. ArrayBlockingQueue:一個有容量限制的隊列.
    5. DelayQueue:一個沒有容量限制的隊列.隊列中的元素必須實現了Delayed接口.元素在隊列中的排序按照當前時間的延遲值,延遲最小/最先要被執行的任務排在隊列頭部,依次排序.延遲時間到達後執行指定任務.
    6. PriorityBlockingQueue:一個沒有容量限制的隊列.隊列中元素必須實現了Comparable接口.隊列中元素排序依賴元素的天然排序/compareTo的比較結果.
    7. 各類BlockingQueue的問題
      1.SynchronousQueue缺點:由於不具有存儲元素的能力,於是當任務很頻繁時候,爲了防止線程數量超標,咱們每每設置maximumPoolSize是Integer.MAX_VALUE,建立過多線程會致使OOM.《阿里巴巴Java開發手冊》中強調不能使用Executors直接建立線程池,就是對應Android源碼中newCachedThreadPool和newScheduledThreadPool,本質上就是建立了maximumPoolSize爲Integer.MAX_VALUE的ThreadPoolExecutor.
      2.LinkedBlockingQueue由於沒有容量限制,因此咱們使用LinkedBlockingQueue建立ThreadPoolExecutor,設置maximumPoolSize是無心義的,若是線程數量已經達到corePoolSize,且核心線程都在忙,那麼新來的任務會一直被添加到隊列中.只要核心線程無空閒則一直得不到被執行機會.
      3.DelayQueue和PriorityBlockingQueue也具備一樣的問題.因此corePoolSize必須設置合理,不然會致使超出核心線程數量的任務一直得不到機會被執行.這兩類隊列分別適用於定時及優先級明確的任務.

    3.4:RejectedExecutionHandler handler/拒絕策略有4種
    1.hreadPoolExecutor.AbortPolicy:丟棄任務,並拋出RejectedExecutionException異常.ThreadPoolExecutor默認就是使用AbortPolicy.
    2.ThreadPoolExecutor.DiscardPolicy:丟棄任務,但不會拋出異常.
    3.ThreadPoolExecutor.DiscardOldestPolicy:丟棄排在隊列頭部的任務,不拋出異常,並嘗試從新執行任務.
    4.ThreadPoolExecutor.CallerRunsPolicy:丟棄任務,但不拋出異常,並將該任務交給調用此ThreadPoolExecutor的線程執行.

  4. synchronized 的本質

    1. 保證synchronized方法或者代碼塊內部資源/數據的互斥訪問
      • 即同一時間,由同一個Monitor監視的代碼,最多隻有1個線程在訪問
    2. 保證線程之間對監視資源的數據同步.
      • 任何線程在獲取Monitor後,會第一時間將共享內存中的數據複製到本身的緩存中;
      • 任何線程在釋放Monitor後,會第一時間將緩存中的數據複製到共享內存中
  5. volatile

    1. 保證被volatile修飾的成員的操做具備原子性和同步性.至關於簡化版的synchronized
      • 原子性就是線程間互斥訪問
      • 同步性就是線程之間對監視資源的數據同步
    2. volatile生效範圍:基本類型的直接複製賦值 + 引用類型的直接賦值
      //引用類型的直接賦值操做有效
      private volatile User u = U1;
      //修改引用類型的屬性,則不是原子性的,volatile無效
      U1.name = "吊炸天"
      //對引用類型的直接賦值是原子性的
      u = U2;
      
      private volatile int a = 0;
      private int b = 100;
      //volatile沒法實現++/--的原子性
      a++;
      複製代碼
      1. volatile型變量自增操做的隱患
        • volatile類型變量每次在讀取的時候,會越過線程的工做內存,直接從主存中讀取,也就不會產生髒讀
        • ++自增操做,在Java對應的彙編指令有三條
          1. 從主存讀取變量值到cpu寄存器
          2. 寄存器裏的值+1
          3. 寄存器的值寫回主存
        • 若是N個線程同時執行到了第1步,那麼最終變量會損失(N-1).第二步第三步只有一個線程是執行成功.
      2. 對變量的寫操做不依賴於當前值,才能用volatile修飾.
  6. 針對num++這類複合類的操做,可使用java併發包中的原子操做類原子操做類:AtomicInteger AtomicBoolean等來保證其原子性.

    public static AtomicInteger num = new AtomicInteger(0);
    num.incrementAndGet();//原子性的num++,經過循環CAS方式
    複製代碼

2.線程間交互

  1. 一個線程終結另外一個線程
    1. Thread.stop不要用:
      • 由於線程在運行過程當中隨時有可能會被暫停切換到其餘線程,stop的效果至關於切換到其餘線程繼續執行且之後不再會切換回來.咱們執行A.stop的時候,徹底沒法預知A的run方法已經執行了多少,執行百分比徹底不可控.
      下面的代碼,每次執行最後打印的結果都不一樣,即咱們徹底不可預知調用stop時候當前線程執行了百分之多少.
      
      private static void t2(){
          Thread t = new Thread(){
              @Override
              public void run() {
                  for(int i=0;i<1000000;i++){
                      System.out.println(""+i);
                  }
              }
          };
          t.start();
          try {
              Thread.sleep(300);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          t.stop();
      }
      複製代碼
    2. Thread.interrupt:僅僅設置當前線程爲被中斷狀態.在運行的線程依然會繼續運行.
      1. Thread.isInterrupted:獲取當前線程是否被中斷
      2. Thread.interrupted():若是線程A調用了Thread.interrupted()
        1. 若是A以前已經被中斷,調用Thread.interrupted()返回false,A已經不是被中斷狀態
        2. 若是A以前不是被中斷狀態,調用Thread.interrupted()返回true,A變成被中斷狀態.
      3. 單純調用A.interrupt是無效果的,interrupt須要和isInterrupted聯合使用
        • 用於咱們但願線程處於被中斷狀態時結束運行的場景.
        • interrupt和stop比較的優勢:stop後,線程直接結束,咱們徹底沒法控制當前執行到哪裏;
          interrupt後線程默認會繼續執行,咱們經過isInterrupted來獲取被中斷狀態,只有被中斷且知足咱們指定條件才return,能夠精確控制線程的執行百分比.
        private static void t2(){
            Thread t = new Thread(){
                @Override
                public void run() {
                    for(int i=0;i<1000000;i++){
                        //檢查線程是否處於中斷狀態,且檢查是否知足指定條件
                        //若是不知足指定條件,即便處於中斷狀態也繼續執行.
                        if(isInterrupted()&&i>800000){
                            //先作收尾工做
                            //return 結束
                            return;
                        }
                        System.out.println(""+i);
                    }
                }
            };
            t.start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //調用了interrupt後,在run中監查是否已經被打斷,若是已經被打斷,且知足指定條件,
            //就return,線程就執行完了
            t.interrupt();
        }
        
        ......
        799999
        800000
        Process finished with exit code 0
        複製代碼
      4. InterruptedException:
        1. 若是線程A在sleep過程當中被其餘線程調用A.interrupt(),會觸發InterruptedException.
        2. 若是調用A.interrupt()時候,A並不在sleep狀態,後面再調用A.sleep,也會當即拋出InterruptedException.
        private static void t3(){
            Thread thread = new Thread(){
                @Override
                public void run() {
                    long t1 = System.currentTimeMillis();
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        long t2 = System.currentTimeMillis();
                        System.out.println("老子被叫醒了:睡了"+(t2-t1)+"ms");
                        //用於作線程收尾工做,而後return
                        return;
                    }
                    System.out.println("AAAAAAAA");
                }
            };
            thread.start();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread.interrupt();
        }
        
        老子被叫醒了:睡了493ms
        Process finished with exit code 0
        複製代碼
  2. 線程等待:wait,notifyAll,notify
    1. wait,notifyAll,notify是屬於Object的方法.用於線程等待的場景,需用Monitor進行調用
    2. wait:
      • 當1個線程A持有Monitor M.
      • 此時調用M.wait,A會釋放M並處於等待狀態.並記錄A在當前代碼執行的位置Position.
    3. notify:
      • 當調用M.notify(),就會喚醒1個由於調用M.wait()而處於等待狀態的線程
      • 若是有A,B,C--多個線程都是由於調用M.wait()而處於等待狀態,不必定哪一個會被喚醒並嘗試獲取M
    4. notifyAll:
      • 當調用M.notifyAll(),全部由於調用M.wait()而處於等待狀態的線程都被喚醒,一塊兒競爭嘗試獲取M
    5. 調用notify/notifyAll被喚醒並獲取到M的線程A,會接着以前的代碼執行位置Position繼續執行下去
    private String str = null;
    private synchronized void setStr(String str){
        System.out.println("setStr時間:"+System.currentTimeMillis());
        this.str = str;
        notifyAll();
    }
    private synchronized void printStr(){
        while (str==null){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("線程:"+Thread.currentThread().getName()+
        " printStr時間:"+System.currentTimeMillis());
        System.out.println("str:"+str);
    }
    private void t4(){
        (new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                setStr("老子設置一下");
            }
        }).start();
        (new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程:"+Thread.currentThread().getName()+
                " 嘗試printStr時間:"+System.currentTimeMillis());
                printStr();
            }
        }).start();
        (new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程:"+Thread.currentThread().getName()+
                " 嘗試printStr時間:"+System.currentTimeMillis());
                printStr();
            }
        }).start();
    }
    
    線程:Thread-2 嘗試printStr時間:1539247468146
    線程:Thread-1 嘗試printStr時間:1539247468944
    setStr時間:1539247469944
    線程:Thread-1 printStr時間:1539247469944
    str:老子設置一下
    線程:Thread-2 printStr時間:1539247469944
    str:老子設置一下
    複製代碼

3.Executor、 AsyncTask、 HandlerThead、 IntentService 如何選擇

  1. HandlerThead就不要用,HandlerThead設計目的就是爲了主界面死循環刷新界面,無其餘應用場景.
  2. 能用線程池就用線程池,由於最簡單.
  3. 涉及後臺線程推送任務到UI線程,可使用Handler或AsyncTask
  4. Service:就是爲了作後臺任務,不要UI界面,須要持續存活.有複雜的須要長期存活/等待的場景使用Service.
  5. IntentService:屬於Service.當咱們須要使用Service,且須要後臺代碼執行完畢後該Service自動被銷燬,使用IntentService.

4.AsyncTask的內存泄漏

  1. GC Roots:由堆外指向堆內的引用,包括:
    1. Java方法棧幀中的局部變量
    2. 已加載類的靜態變量
    3. native代碼的引用
    4. 運行中的Java線程
  2. AsyncTask內存泄漏本質:正在運行的線程/AsyncTask 在虛擬機中屬於GC ROOTS,AsyncTask持有外部Activity的引用.被GC ROOTS引用的對象不能被回收.
  3. 因此AsyncTask和其餘線程工具同樣,只要是使用線程,都有可能發生內存泄漏,都要及時關閉,AsyncTask並不比其餘工具更差.
  4. 如何避免AsyncTask內存泄漏:使用弱引用解決AsyncTask在Activity銷燬後依然持有Activity引用的問題

5.RxJava.

講的太多了這裏推薦1個專題RxJava2.x
下面記錄一下本身不太熟的幾點

  1. RxJava總體結構:
    1. 鏈的最上游:生產者Observable
    2. 鏈的最下游:觀察者Observer
    3. 鏈的中間多個節點:雙重角色.便是上一節點的觀察者Observer,也是下一節點的生產者Observable.
  2. Scheduler切換線程的原理:源碼跟蹤下去,實質是經過Excutor實現了線程切換.

6.Android M對Profile GPU Rendering工具的更新

  1. Swap Buffers:CPU等待GPU處理的時間
  2. Command Issur:OpenGL渲染Display List所須要的時間
  3. Sync&Upload:一般表示的是準備當前界面上有待繪製的圖片所耗費的時間,爲了減小該段區域的執行時間,咱們能夠減小屏幕上的圖片數量或者是縮小圖片自己的大小
  4. Draw:測量繪製Display List的時間
  5. Measure & Layout:這裏表示的是佈局的onMeasure與onLayout所花費的時間.一旦時間過長,就須要仔細檢查本身的佈局是否是存在嚴重的性能問題
  6. Animation:表示的是計算執行動畫所須要花費的時間.包含的動畫有ObjectAnimator,ViewPropertyAnimator,Transition等等.一旦這裏的執行時間過長,就須要檢查是否是使用了非官方的動畫工具或者是檢查動畫執行的過程當中是否是觸發了讀寫操做等等
  7. Input Handling:表示的是系統處理輸入事件所耗費的時間,粗略等於對於的事件處理方法所執行的時間.一旦執行時間過長,意味着在處理用戶的輸入事件的地方執行了複雜的操做
  8. Misc/Vsync Delay:若是稍加註意,咱們能夠在開發應用的Log日誌裏面看到這樣一行提示:I/Choreographer(691): Skipped XXX frames! The application may be doing too much work on its main thread。這意味着咱們在主線程執行了太多的任務,致使UI渲染跟不上vSync的信號而出現掉幀的狀況

9.Android性能優化典範-第6季

1.啓動閃屏

  1. 當點擊桌面圖標啓動APP的時候,App會出現短暫的白屏,一直到第一個Activity的頁面的渲染加載完畢
  2. 爲了消除白屏,咱們能夠爲App入口Activity單獨設置theme.
    1. 在單獨設置的theme中設置android:background屬性爲App的品牌宣傳圖片背景.
    2. 在代碼執行到入口Activity的onCreate的時候設置爲程序正常的主題.
    styles.xml
    <!-- Base application theme. -->
    //Activity默認主題
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> //默認主題窗口背景設置爲白色 <item name="android:background">@android:color/white</item> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:windowNoTitle">true</item> <item name="android:windowFullscreen">true</item> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
    //入口Activity的theme單獨設置
    <style name="ThemeSplash" parent="Theme.AppCompat.Light.NoActionBar"> //入口Activity初始窗口背景設置爲品牌宣傳圖片 <item name="android:background">@mipmap/startbg</item> <item name="android:windowNoTitle">true</item> <item name="android:windowFullscreen">true</item> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
    
    AndroidManifest.xml
    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity" android:theme="@style/ThemeSplash">//爲入口Activity單獨指定theme
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </manifest>
    
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            //在代碼執行到入口Activity時候設置入口Activity爲默認主題
            setTheme(R.style.AppTheme);
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tv_all = findViewById(R.id.tv_all);
            tv_local = findViewById(R.id.tv_local);
            //註冊全局廣播
            registerReceiver(globalReceiver,new IntentFilter("global"));
            //註冊本地廣播
            LocalBroadcastManager.getInstance(this).registerReceiver(localBroadReceiver,new IntentFilter("localBroadCast"));
        }
    }
    複製代碼

2.爲App提供對應分辨率下的圖片,系統會自動匹配最合適分辨率的圖片執行拉伸或壓縮的處理.

  1. 若是是隻有1張圖片,放在mipmap-nodpi,或mipmap-xxxhdpi下
  2. 全部的大背景圖片,統一放在mipmap-nodpi目錄,用一套1080P素材能夠解決大部分手機適配問題,不用每一個資源目錄下放一套素材
  3. 通過試驗,不論ImageView寬高是不是wrap_content,只要圖片所在文件夾和當前設備分辨率不匹配,都會涉及到放大或壓縮,佔用的內存都會相應的變化.尤爲對於大圖,放在低分辨率文件夾下直接OOM.
    具體緣由:
    郭霖:Android drawable微技巧,你所不知道的drawable的那些細節

當咱們使用資源id來去引用一張圖片時,Android會使用一些規則來去幫咱們匹配最適合的圖片。什麼叫最適合的圖片?好比個人手機屏幕密度是xxhdpi,那麼drawable-xxhdpi文件夾下的圖片就是最適合的圖片。所以,當我引用android_logo這張圖時,若是drawable-xxhdpi文件夾下有這張圖就會優先被使用,在這種狀況下,圖片是不會被縮放的。可是,若是drawable-xxhdpi文件夾下沒有這張圖時, 系統就會自動去其它文件夾下找這張圖了,優先會去更高密度的文件夾下找這張圖片,咱們當前的場景就是drawable-xxxhdpi文件夾,而後發現這裏也沒有android_logo這張圖,接下來會嘗試再找更高密度的文件夾,發現沒有更高密度的了,這個時候會去drawable-nodpi文件夾找這張圖,發現也沒有,那麼就會去更低密度的文件夾下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi。 整體匹配規則就是這樣,那麼好比說如今終於在drawable-mdpi文件夾下面找到android_logo這張圖了,可是系統會認爲你這張圖是專門爲低密度的設備所設計的,若是直接將這張圖在當前的高密度設備上使用就有可能會出現像素太低的狀況,因而系統自動幫咱們作了這樣一個放大操做。 那麼一樣的道理,若是系統是在drawable-xxxhdpi文件夾下面找到這張圖的話,它會認爲這張圖是爲更高密度的設備所設計的,若是直接將這張圖在當前設備上使用就有可能會出現像素太高的狀況,因而會自動幫咱們作一個縮小的操做

3.儘可能複用已經存在的圖片.

好比一張圖片O已經存在,若是有View的背景就是O旋轉事後的樣子,能夠直接用O建立RotateDrawable.而後將設置給View使用.
注意:RotateDrawable已經重寫了其onLevelChange方法,因此必定要設置level纔會生效

@Override
protected boolean onLevelChange(int level) {
    super.onLevelChange(level);

    final float value = level / (float) MAX_LEVEL;
    final float degrees = MathUtils.lerp(mState.mFromDegrees, mState.mToDegrees, value);
    mState.mCurrentDegrees = degrees;

    invalidateSelf();
    return true;
}
複製代碼

實例:

1.首先建立xml文件
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@mipmap/close10" android:fromDegrees="90" android:toDegrees="120" android:pivotX="50%" android:pivotY="50%" >
</rotate>

2.在Java代碼中獲取該xml對應的Drawable實例,並設置level爲10000
Drawable drawable = getResources().getDrawable(R.drawable.rotate_close);
drawable.setLevel(10000);
3.將Drawable設置爲View的背景
findViewById(R.id.v).setBackgroundDrawable(drawable);
複製代碼

4.開啓混淆和資源壓縮:在app模塊下的的build.gradle中

buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
複製代碼

5.對於簡單/規則紋理的圖片,使用VectorDrawable來替代多個分辨率圖片.VectorDrawable 有不少注意事項,後面單獨一篇文章總結

10.網絡優化

網絡優化主要有幾個方面:下降網絡請求數量,下降單次請求響應的數據量,在弱網環境下將非必要網絡請求延緩至網絡環境好的時候.

1.下降網絡請求數量:獲取一樣的數據,屢次網絡請求會增長電量消耗,且屢次請求整體上將消耗服務端更多的時間及資源

  1. 接口Api設計要合理.能夠將多個接口合併,屢次請求顯示1個界面,改造後1個接口便可提供完整數據.
  2. 根據具體場景實時性需求,在App中加入網絡緩存,在實時性有效區間避免重複請求:主要包括網絡框架和圖片加載框架的緩存.

2.下降單次請求的數據量

  1. 網絡接口Api在設計時候,去除多餘的請求參數及響應數據.
  2. 網絡請求及響應數據的傳輸開啓GZIP壓縮,下降傳輸數據量.
    • okHttp對gzip的支持前面已記錄
  3. Protocal Buffers,Nano-Proto-Buffers,FlatBuffers代替GSON執行序列化.
    • Protocal Buffers網上有使用的方法,相對GSON有點繁瑣.若是對網絡傳輸量很敏感,能夠考慮使用.其餘幾種方案的文章很少.
  4. 網絡請求圖片,添加圖片寬高參數,避免下載過大圖片增長流量消耗.

3.弱網環境優化這塊沒有經驗,直接看anly_jun的文章

App優化之網絡優化
文章中提到:用戶點贊操做, 能夠直接給出界面的點同意功的反饋, 使用JobScheduler在網絡狀況較好的時候打包請求.

11.電量優化

anly_jun大神的:App優化之電池省着用

12.JobScheduler,AlarmManager和WakeLock

JobScheduler在網絡優化中出現過,WakeLock涉及電量優化,AlarmManager和WakeLock有類似,但側重點不一樣.

  1. WakeLock:好比一段關鍵邏輯T已經在執行,執行未完成Android系統就進入休眠,會致使T執行中斷.WakeLock目的就在於阻止Android系統進入休眠狀態,保證T得以繼續執行.
    • 休眠過程當中自定義的Timer、Handler、Thread、Service等都會暫停
  2. AlarmManager:Android系統自帶的定時器,能夠將處於休眠狀態的Android系統喚醒
    • 保證Android系統在休眠狀態下被及時喚醒,執行 定時/延時/輪詢任務
  3. JobScheduler:JobScheduler目的在於將當下不緊急的任務延遲到後面更合適的某個時間來執行.咱們能夠控制這些任務在什麼條件下被執行.
    • JobScheduler能夠節約Android設備當下網絡,電量,CPU等資源.在指定資源充裕狀況下再執行"不緊要"的任務.

JobScheduler:
Android Jobscheduler使用
Android開發筆記(一百四十三)任務調度JobScheduler
WakeLock:
Android WakeLock詳解
Android PowerManager.WakeLock使用小結
Android的PowerManager和PowerManager.WakeLock用法簡析
AlarmManager和WakeLock使用:
後臺任務 - 保持設備喚醒狀態

13.性能檢測工具

1.Android Studio 3.2以後,Android Device Monitor已經被移除.Android Device Monitor原先包含的工具由新的方案替代.Android Device Monitor

  1. DDMS:由Android Profiler代替.能夠進行CPU,內存,網絡分析.
  2. TraceView:能夠經過Debug類在代碼中調用Debug.startMethodTracing(String tracePath)和Debug.stopMethodTracing()來記錄二者之間全部線程及線程中方法的耗時,生成.trace文件.經過abd命令能夠將trace文件導出到電腦,經過CPU profiler分析.
  3. Systrace:能夠經過命令行生成html文件,經過Chrome瀏覽器進行分析.
    • 生成html文件已實現.但文件怎麼分析暫未掌握,看了網上一些文章說實話仍是沒搞懂
  4. Hierarchy Viewer:由Layout Inspector代替.但當前版本的Layout Inspector不能查看每一個View具體的onMeasure,onLayout,onDraw耗時,功能是不足的.咱們可以使用系統提供的Window.OnFrameMetricsAvailableListener來計算指定View的onLayout及onDraw耗時.
  5. Network Traffic tool:由Network Profiler代替.

2.其中Android Profiler如何使用,直接看官網便可.Profile your app performance.

3.TraceView

  1. TraceView能夠直接經過CPU profiler中點擊Record按鈕後,任意時間後點擊Stop按鈕.便可生成trace文件.並可將.trace文件導出.
  2. TraceView也能夠經過Debug類在代碼中精確控制要統計哪一個區間代碼/線程的CPU耗時.
    這種用法是 anly_jun大神文章裏學到的
    public class SampleApplication extends Application {
        @Override
        public void onCreate() {
            Debug.startMethodTracing("JetApp");
            super.onCreate();
            LeakCanary.install(this);
            // init logger.
            AppLog.init();
            // init crash helper
            CrashHelper.init(this);
            // init Push
            PushPlatform.init(this);
            // init Feedback
            FeedbackPlatform.init(this);
            Debug.stopMethodTracing();
        }
    複製代碼
    代碼執行完畢,會在Android設備中生成JetApp.trace文件.經過Device File Explorer,找到sdcard/Android/data/app包名/files/JetApp.trace
    在JetApp.trace上點擊右鍵->Copy Path,將trace文件路徑複製下來.
    Windows下cmd打開命令行,執行 adb pull 路徑,便可trace文件導出到電腦.
  3. trace文件分析很簡單.咱們能夠看到每一個線程及線程中每一個方法調用消耗的時間.

4.Layout Inspector很簡單,在App運行後,點擊Tools->Layout Inspector便可.

下面只看Window.OnFrameMetricsAvailableListener怎麼用.

從Android 7.0 (API level 24)開始,Android引入Window.OnFrameMetricsAvailableList接口用於提供每一幀繪製各階段的耗時,數據源與GPU Profile相同.

public interface OnFrameMetricsAvailableListener {
    void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,int dropCountSinceLastInvocation);
}
/** * 包含1幀的週期內,渲染系統各個方法的耗時數據. */
public final class FrameMetrics {
    ****
    //經過getMetric獲取layout/measure耗時所用的id
    public static final int LAYOUT_MEASURE_DURATION = 3;
    public static final int DRAW_DURATION = 4;
    /** * 獲取當前幀指定id表明的方法/過程的耗時,單位是納秒:1納秒(ns)=10的負6次方毫秒(ms) */
    public long getMetric(@Metric int id) {
        ****
    }
}
複製代碼
  1. 在Activity中使用OnFrameMetricsAvailableListener:
    • 經過調用this.getWindow().addOnFrameMetricsAvailableListener(@NonNull OnFrameMetricsAvailableListener listener,Handler handler)來添加監聽.
    • 經過調用this.getWindow().removeOnFrameMetricsAvailableListener(OnFrameMetricsAvailableListener listener)取消監聽.
    • 解析ConstraintLayout的性能優點中引用了Google使用OnFrameMetricsAvailableListener的例子android-constraint-layout-performance.其中Activity是Kotlin寫的,嘗試將代碼轉爲java,解決掉報錯後運行.
      package p1.com.p1;
      
      import android.os.AsyncTask;
      import android.os.Bundle;
      import android.os.Handler;
      import android.support.annotation.RequiresApi;
      import android.support.v7.app.AppCompatActivity;
      import android.util.Log;
      import android.view.FrameMetrics;
      import android.view.View;
      import android.view.View.MeasureSpec;
      import android.view.View.OnClickListener;
      import android.view.ViewGroup;
      import android.view.Window;
      import android.view.Window.OnFrameMetricsAvailableListener;
      import android.widget.Button;
      import android.widget.TextView;
      import org.jetbrains.annotations.NotNull;
      import org.jetbrains.annotations.Nullable;
      import java.lang.ref.WeakReference;
      import java.util.Arrays;
      import kotlin.TypeCastException;
      import kotlin.jvm.internal.Intrinsics;
      
      public final class KtMainActivity extends AppCompatActivity {
          private final Handler frameMetricsHandler = new Handler();
          @RequiresApi(24)
          private final OnFrameMetricsAvailableListener frameMetricsAvailableListener = new OnFrameMetricsAvailableListener() {
              @Override
              public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
                  long costDuration = frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);
                  Log.d("Jet", "layoutMeasureDurationNs: " + costDuration);
              }
          };
          private static final String TAG = "KtMainActivity";
          private static final int TOTAL = 100;
          private static final int WIDTH = 1920;
          private static final int HEIGHT = 1080;
      
          @RequiresApi(3)
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              this.setContentView(R.layout.activity_for_test);
              final Button traditionalCalcButton = (Button) this.findViewById(R.id.button_start_calc_traditional);
              final Button constraintCalcButton = (Button) this.findViewById(R.id.button_start_calc_constraint);
              final TextView textViewFinish = (TextView) this.findViewById(R.id.textview_finish);
              traditionalCalcButton.setOnClickListener((OnClickListener) (new OnClickListener() {
                  public final void onClick(View it) {
                      Button var10000 = constraintCalcButton;
                      Intrinsics.checkExpressionValueIsNotNull(constraintCalcButton, "constraintCalcButton");
                      var10000.setVisibility(View.INVISIBLE);
                      View var4 = KtMainActivity.this.getLayoutInflater().inflate(R.layout.activity_traditional, (ViewGroup) null);
                      if (var4 == null) {
                          throw new TypeCastException("null cannot be cast to non-null type android.view.ViewGroup");
                      } else {
                          ViewGroup container = (ViewGroup) var4;
                          String var10002 = KtMainActivity.this.getString(R.string.executing_nth_iteration);
                          Intrinsics.checkExpressionValueIsNotNull(var10002, "getString(R.string.executing_nth_iteration)");
                          KtMainActivity.MeasureLayoutAsyncTask asyncTask = new KtMainActivity.MeasureLayoutAsyncTask(var10002, new WeakReference(traditionalCalcButton), new WeakReference(textViewFinish), new WeakReference(container));
                          asyncTask.execute(new Void[0]);
                      }
                  }
              }));
              constraintCalcButton.setOnClickListener((OnClickListener) (new OnClickListener() {
                  public final void onClick(View it) {
                      Button var10000 = traditionalCalcButton;
                      Intrinsics.checkExpressionValueIsNotNull(traditionalCalcButton, "traditionalCalcButton");
                      var10000.setVisibility(View.INVISIBLE);
                      View var4 = KtMainActivity.this.getLayoutInflater().inflate(R.layout.activity_constraintlayout, (ViewGroup) null);
                      if (var4 == null) {
                          throw new TypeCastException("null cannot be cast to non-null type android.view.ViewGroup");
                      } else {
                          ViewGroup container = (ViewGroup) var4;
                          String var10002 = KtMainActivity.this.getString(R.string.executing_nth_iteration);
                          Intrinsics.checkExpressionValueIsNotNull(var10002, "getString(R.string.executing_nth_iteration)");
                          KtMainActivity.MeasureLayoutAsyncTask asyncTask = new KtMainActivity.MeasureLayoutAsyncTask(var10002, new WeakReference(constraintCalcButton), new WeakReference(textViewFinish), new WeakReference(container));
                          asyncTask.execute(new Void[0]);
                      }
                  }
              }));
          }
          @RequiresApi(24)
          protected void onResume() {
              super.onResume();
              this.getWindow().addOnFrameMetricsAvailableListener(this.frameMetricsAvailableListener, this.frameMetricsHandler);
          }
          @RequiresApi(24)
          protected void onPause() {
              super.onPause();
              this.getWindow().removeOnFrameMetricsAvailableListener(this.frameMetricsAvailableListener);
          }
          @RequiresApi(3)
          private static final class MeasureLayoutAsyncTask extends AsyncTask {
              @NotNull
              private final String executingNthIteration;
              @NotNull
              private final WeakReference startButtonRef;
              @NotNull
              private final WeakReference finishTextViewRef;
              @NotNull
              private final WeakReference containerRef;
      
              @Nullable
              protected Void doInBackground(@NotNull Void... voids) {
                  Intrinsics.checkParameterIsNotNull(voids, "voids");
                  int i = 0;
      
                  for (int var3 = KtMainActivity.TOTAL; i < var3; ++i) {
                      this.publishProgress(new Integer[]{i});
      
                      try {
                          Thread.sleep(100L);
                      } catch (InterruptedException var5) {
                          ;
                      }
                  }
                  return null;
              }
              // $FF: synthetic method
              // $FF: bridge method
              public Object doInBackground(Object[] var1) {
                  return this.doInBackground((Void[]) var1);
              }
              protected void onProgressUpdate(@NotNull Integer... values) {
                  Intrinsics.checkParameterIsNotNull(values, "values");
                  Button var10000 = (Button) this.startButtonRef.get();
                  if (var10000 != null) {
                      Button startButton = var10000;
                      Intrinsics.checkExpressionValueIsNotNull(startButton, "startButton");
      // StringCompanionObject var3 = StringCompanionObject.INSTANCE;
                      String var4 = this.executingNthIteration;
                      Object[] var5 = new Object[]{values[0], KtMainActivity.TOTAL};
                      String var9 = String.format(var4, Arrays.copyOf(var5, var5.length));
                      Intrinsics.checkExpressionValueIsNotNull(var9, "java.lang.String.format(format, *args)");
                      String var7 = var9;
                      startButton.setText((CharSequence) var7);
                      ViewGroup var10 = (ViewGroup) this.containerRef.get();
                      if (var10 != null) {
                          ViewGroup container = var10;
                          Intrinsics.checkExpressionValueIsNotNull(container, "container");
                          this.measureAndLayoutExactLength(container);
                          this.measureAndLayoutWrapLength(container);
                      }
                  }
              }
              // $FF: synthetic method
              // $FF: bridge method
              public void onProgressUpdate(Object[] var1) {
                  this.onProgressUpdate((Integer[]) var1);
              }
              protected void onPostExecute(@Nullable Void aVoid) {
                  TextView var10000 = (TextView) this.finishTextViewRef.get();
                  if (var10000 != null) {
                      TextView finishTextView = var10000;
                      Intrinsics.checkExpressionValueIsNotNull(finishTextView, "finishTextView");
                      finishTextView.setVisibility(View.VISIBLE);
                      Button var4 = (Button) this.startButtonRef.get();
                      if (var4 != null) {
                          Button startButton = var4;
                          Intrinsics.checkExpressionValueIsNotNull(startButton, "startButton");
                          startButton.setVisibility(View.GONE);
                      }
                  }
              }
              // $FF: synthetic method
              // $FF: bridge method
              public void onPostExecute(Object var1) {
                  this.onPostExecute((Void) var1);
              }
              private final void measureAndLayoutWrapLength(ViewGroup container) {
                  int widthMeasureSpec = MeasureSpec.makeMeasureSpec(KtMainActivity.WIDTH, View.MeasureSpec.AT_MOST);
                  int heightMeasureSpec = MeasureSpec.makeMeasureSpec(KtMainActivity.HEIGHT, View.MeasureSpec.AT_MOST);
                  container.measure(widthMeasureSpec, heightMeasureSpec);
                  container.layout(0, 0, container.getMeasuredWidth(), container.getMeasuredHeight());
              }
      
              private final void measureAndLayoutExactLength(ViewGroup container) {
                  int widthMeasureSpec = MeasureSpec.makeMeasureSpec(KtMainActivity.WIDTH, View.MeasureSpec.EXACTLY);
                  int heightMeasureSpec = MeasureSpec.makeMeasureSpec(KtMainActivity.HEIGHT, View.MeasureSpec.EXACTLY);
                  container.measure(widthMeasureSpec, heightMeasureSpec);
                  container.layout(0, 0, container.getMeasuredWidth(), container.getMeasuredHeight());
              }
      
              @NotNull
              public final String getExecutingNthIteration() {
                  return this.executingNthIteration;
              }
      
              @NotNull
              public final WeakReference getStartButtonRef() {
                  return this.startButtonRef;
              }
      
              @NotNull
              public final WeakReference getFinishTextViewRef() {
                  return this.finishTextViewRef;
              }
      
              @NotNull
              public final WeakReference getContainerRef() {
                  return this.containerRef;
              }
      
              public MeasureLayoutAsyncTask(@NotNull String executingNthIteration, @NotNull WeakReference startButtonRef, @NotNull WeakReference finishTextViewRef, @NotNull WeakReference containerRef) {
                  super();
                  Intrinsics.checkParameterIsNotNull(executingNthIteration, "executingNthIteration");
                  Intrinsics.checkParameterIsNotNull(startButtonRef, "startButtonRef");
                  Intrinsics.checkParameterIsNotNull(finishTextViewRef, "finishTextViewRef");
                  Intrinsics.checkParameterIsNotNull(containerRef, "containerRef");
                  this.executingNthIteration = executingNthIteration;
                  this.startButtonRef = startButtonRef;
                  this.finishTextViewRef = finishTextViewRef;
                  this.containerRef = containerRef;
              }
          }
      }
      
      D/Jet: layoutMeasureDurationNs: 267344
      D/Jet: layoutMeasureDurationNs: 47708
      D/Jet: layoutMeasureDurationNs: 647240
      D/Jet: layoutMeasureDurationNs: 59636
      D/Jet: layoutMeasureDurationNs: 50052
      D/Jet: layoutMeasureDurationNs: 49739
      D/Jet: layoutMeasureDurationNs: 75990
      D/Jet: layoutMeasureDurationNs: 296198
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 894375
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 1248021
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 0
      D/Jet: layoutMeasureDurationNs: 1290677
      D/Jet: layoutMeasureDurationNs: 2936563
      D/Jet: layoutMeasureDurationNs: 1387188
      D/Jet: layoutMeasureDurationNs: 2325521
      D/Jet: layoutMeasureDurationNs: 1940052
      D/Jet: layoutMeasureDurationNs: 1539271
      D/Jet: layoutMeasureDurationNs: 803750
      D/Jet: layoutMeasureDurationNs: 1405000
      D/Jet: layoutMeasureDurationNs: 1188437
      D/Jet: layoutMeasureDurationNs: 1748802
      D/Jet: layoutMeasureDurationNs: 3422240
      D/Jet: layoutMeasureDurationNs: 1400677
      D/Jet: layoutMeasureDurationNs: 2416094
      D/Jet: layoutMeasureDurationNs: 1532864
      D/Jet: layoutMeasureDurationNs: 1684063
      D/Jet: layoutMeasureDurationNs: 1092865
      D/Jet: layoutMeasureDurationNs: 1363177
      D/Jet: layoutMeasureDurationNs: 1067188
      D/Jet: layoutMeasureDurationNs: 1358333
      D/Jet: layoutMeasureDurationNs: 2999895
      D/Jet: layoutMeasureDurationNs: 2113021
      D/Jet: layoutMeasureDurationNs: 1957395
      D/Jet: layoutMeasureDurationNs: 1319740
      D/Jet: layoutMeasureDurationNs: 2207239
      D/Jet: layoutMeasureDurationNs: 1514167
      D/Jet: layoutMeasureDurationNs: 949114
      D/Jet: layoutMeasureDurationNs: 1691250
      D/Jet: layoutMeasureDurationNs: 1387448
      D/Jet: layoutMeasureDurationNs: 932552
      D/Jet: layoutMeasureDurationNs: 1223802
      D/Jet: layoutMeasureDurationNs: 2024740
      D/Jet: layoutMeasureDurationNs: 1242292
      D/Jet: layoutMeasureDurationNs: 2228230
      D/Jet: layoutMeasureDurationNs: 1382083
      D/Jet: layoutMeasureDurationNs: 2233282
      D/Jet: layoutMeasureDurationNs: 1907187
      D/Jet: layoutMeasureDurationNs: 2287552
      D/Jet: layoutMeasureDurationNs: 776354
      D/Jet: layoutMeasureDurationNs: 1225000
      D/Jet: layoutMeasureDurationNs: 875417
      D/Jet: layoutMeasureDurationNs: 1271302
      D/Jet: layoutMeasureDurationNs: 1211614
      D/Jet: layoutMeasureDurationNs: 1346459
      D/Jet: layoutMeasureDurationNs: 1978854
      D/Jet: layoutMeasureDurationNs: 2915677
      D/Jet: layoutMeasureDurationNs: 1330573
      D/Jet: layoutMeasureDurationNs: 2195364
      D/Jet: layoutMeasureDurationNs: 775208
      D/Jet: layoutMeasureDurationNs: 2492292
      D/Jet: layoutMeasureDurationNs: 400104
      D/Jet: layoutMeasureDurationNs: 2844375
      D/Jet: layoutMeasureDurationNs: 1563750
      D/Jet: layoutMeasureDurationNs: 3689531
      D/Jet: layoutMeasureDurationNs: 2019323
      D/Jet: layoutMeasureDurationNs: 1663906
      D/Jet: layoutMeasureDurationNs: 1004531
      D/Jet: layoutMeasureDurationNs: 738125
      D/Jet: layoutMeasureDurationNs: 1299166
      D/Jet: layoutMeasureDurationNs: 1223854
      D/Jet: layoutMeasureDurationNs: 1942240
      D/Jet: layoutMeasureDurationNs: 1392396
      D/Jet: layoutMeasureDurationNs: 1906458
      D/Jet: layoutMeasureDurationNs: 691198
      D/Jet: layoutMeasureDurationNs: 2620468
      D/Jet: layoutMeasureDurationNs: 1953229
      D/Jet: layoutMeasureDurationNs: 1120365
      D/Jet: layoutMeasureDurationNs: 3165417
      D/Jet: layoutMeasureDurationNs: 537709
      D/Jet: layoutMeasureDurationNs: 3019531
      D/Jet: layoutMeasureDurationNs: 706250
      D/Jet: layoutMeasureDurationNs: 1129115
      D/Jet: layoutMeasureDurationNs: 539427
      D/Jet: layoutMeasureDurationNs: 1633438
      D/Jet: layoutMeasureDurationNs: 1784479
      D/Jet: layoutMeasureDurationNs: 743229
      D/Jet: layoutMeasureDurationNs: 1851615
      D/Jet: layoutMeasureDurationNs: 851927
      D/Jet: layoutMeasureDurationNs: 1847916
      D/Jet: layoutMeasureDurationNs: 836718
      D/Jet: layoutMeasureDurationNs: 2892552
      D/Jet: layoutMeasureDurationNs: 1230573
      D/Jet: layoutMeasureDurationNs: 3886563
      D/Jet: layoutMeasureDurationNs: 2138281
      D/Jet: layoutMeasureDurationNs: 2198021
      D/Jet: layoutMeasureDurationNs: 1805885
      D/Jet: layoutMeasureDurationNs: 2316927
      D/Jet: layoutMeasureDurationNs: 1990937
      D/Jet: layoutMeasureDurationNs: 2261041
      D/Jet: layoutMeasureDurationNs: 2159010
      D/Jet: layoutMeasureDurationNs: 666562
      D/Jet: layoutMeasureDurationNs: 2332031
      D/Jet: layoutMeasureDurationNs: 1061875
      D/Jet: layoutMeasureDurationNs: 1879062
      D/Jet: layoutMeasureDurationNs: 1411459
      D/Jet: layoutMeasureDurationNs: 154635
      複製代碼
  2. 在Application中使用OnFrameMetricsAvailableListener,則能夠統一設置,不須要每一個Activity單獨設置,推薦使用開源項目ActivityFrameMetrics
    1. 在Application的onCreate中設置單幀渲染總時間超過W毫秒和E毫秒,會在Logcat中打印警告和錯誤的Log信息.
      public class SampleApplication extends Application {
          @Override
          public void onCreate() {
              registerActivityLifecycleCallbacks(new ActivityFrameMetrics.Builder()
                      .warningLevelMs(10)     //default: 17ms
                      .errorLevelMs(10)       //default: 34ms
                      .showWarnings(true)     //default: true
                      .showErrors(true)       //default: true
                      .build());
          }
      }
      複製代碼
    2. Application設置完成運行App,出現單幀渲染總耗時超過指定時間,便可看到Logcat中的信息.
      E/FrameMetrics: Janky frame detected on KtMainActivity with total duration: 16.91ms
      Layout/measure: 1.66ms, draw:2.51ms, gpuCommand:3.13ms others:9.61ms
      Janky frames: 72/107(67.28972%)
      E/FrameMetrics: Janky frame detected on KtMainActivity with total duration: 15.47ms
      Layout/measure: 1.00ms, draw:2.05ms, gpuCommand:3.44ms others:8.98ms
      Janky frames: 73/108(67.59259%)
      E/FrameMetrics: Janky frame detected on KtMainActivity with total duration: 15.09ms
      Layout/measure: 1.30ms, draw:1.44ms, gpuCommand:2.91ms others:9.44ms
      Janky frames: 74/110(67.27273%)
      ****
      複製代碼

5.Systrace:經過命令行生成html文件,經過Chrome瀏覽器進行分析

  1. 首先電腦要安裝python,這裏有幾個坑:
    1. Python要安裝2.7x版本,不能安裝最新的3.x.
      • 好比本身電腦中systrace文件夾路徑是:C:\Users\你的用戶名\AppData\Local\Android\Sdk\platform-tools\systrace,若是咱們安裝的是3.x版本,在這個路徑下執行python systrace.py *** 命令會報錯,提示你應該安裝2.7
    2. Python安裝時候,要記得勾選"Add python.exe to Path".
    3. 這時候直接執行python systrace.py ***命令仍是會報錯:ImportError: No module named win32com
  2. 生成html及如何分析
相關文章
相關標籤/搜索