Android卡頓優化 | 卡頓及其優化工具概述及StrictMode實踐案例

項目GitHub

本文要點

  • 一般使用的卡頓優化工具
  • 卡頓問題概述
  • 卡頓問題分析難點
  • 關於CPU Profiler
  • 關於Systrace
  • 關於StrictMode
  • 磁盤讀寫違例檢測實戰
  • 實例限制檢測實戰

一般使用的卡頓優化工具

  • CPU Profiler
  • Systrace
  • StrictMode
    (strict adj.精確的; 絕對的; 嚴格的,嚴謹的; [植]筆直的
    mode n.方式; 狀況; 時尚,風尚; 調式 模式;)

卡頓問題概述

  • 很多性能問題(如內存佔用高、耗費流量等)都相對不容易被發現,
    但是卡頓問題卻是很容易被直觀感受到的;
  • 卡頓問題較難排查、定位;

卡頓問題分析難點

  • 可能的產生原因 繁雜:代碼、內存、繪製、IO、【在主線程做UI處理、IO操作耗時操作】等;
  • 線上卡頓問題,在線下難以復現,
    卡頓問題跟用戶屆時的現場環境有很大的關係;
  • 比如,
    屆時用戶終端的磁盤IO空間不足,影響了APP的IO寫入性能,
    導致APP卡頓,這樣的場景有時候是很難復現的;
    【最好在問題發生時候,就記錄下來用戶屆時的場景】

關於CPU Profiler

  • 圖形的形式展示程序的執行時間、調用棧、執行次數等;

  • 信息全面,包含了所有線程、所有方法的調用時間;

  • 運行時開銷比較嚴重,導致APP運行時所有函數都會不等比地變慢,可能會帶偏優化方向;

  • 使用方式

    • Debug.startMethodTracing();【在需要監控的代碼塊前添加(注意它有四個重載方法)】
    • Debug.stopMethodTracing();【在需要監控的代碼塊後添加】
    • 生成的調試文件在sd卡:Android/data/packagename/files
    • 上次在內存優化的實戰中,
      其實已經使用過,提到過CPU Profiler了,
      這裏可以看一下App內存優化 之 內存抖動解決實戰!!!!!!

關於Systrace

關於StrictMode

  • 嚴苛模式,Android提供的一種運行時檢測機制;
    如果在開發階段對成千上萬行的代碼進行code review,
    可能效率是比較低下的;
    使用StrictMode之後,
    系統會自動檢測出來主線程當中違例的一些情況,
    同時按照代碼的配置給出相應的反應

  • 方便,強大,容易被忽視

  • 主要檢測:線程檢測策略、虛擬機檢測策略

    • 線程檢測策略【StrictMode.setThreadPolicy()】:
      如,
      自定義的耗時調用檢測,如detectCustomSlowCalls()
      磁盤讀取操作檢測,detectDiskReads()
      網絡操作檢測,detectNetwork()
      【detect vt.查明,發現; 洞察; 偵察,偵查; 】
    • 虛擬機策略【StrictMode.setVmPolicy()】:
      Activity泄漏檢測,detectActivityLeaks()
      SqlLite對象泄漏檢測,detectLeakedSqlLiteObjects()
      限制實例數量檢測,setClassInstanceLimit(要限制的類實例,限制的數量)
  • 具體使用:
    可以在Activity或者ApplicationonCreate()中調用StrictMode的方法:

private boolean DEV_MODE = true;

    private void initStrictMode() {
        if (DEV_MODE) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectCustomSlowCalls() //API等級11,使用StrictMode.noteSlowCode
                    .detectDiskReads()
                    .detectDiskWrites()
                    .detectNetwork()// or .detectAll() for all detectable problems
                    .penaltyLog() //在Logcat 中打印違規異常信息
                    .build());

            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectLeakedSqlLiteObjects()
                    .setClassInstanceLimit(NewsItem.class, 1)
                    .detectLeakedClosableObjects() //API等級11
                    .penaltyLog()
                    .build());
        }
    }
  • 【調試技巧】
    設置一個DEV_MODE標誌位:
    只有在線下開發的環境時將之設置爲true,纔會使進程打開StrictMode

  • 【檢測策略的調用】
    detect開頭的方法,
    都是StrictMode提供的檢測策略,
    調用過了,則StrictMode便會進行相應的檢測和反應;

  • 【響應方式配置】
    penaltyLog()【penalty n.懲罰,刑罰,害處】是出現違規後用log打印出來,即指定StrictMode的響應方式,
    StrictMode除了打印log的方式,
    還有其他響應方式,
    penaltyDeath()可以讓APP直接崩潰掉,
    penaltyDialog()可以彈出一個Dialog等!!!!!!!!!

    9125154-5b912c4082ab6840.png

  • 實戰一下:
    磁盤讀寫違例檢測(log的響應方式):

/**
 * 模擬內存泄露的Activity
 */
public class MemoryLeakActivity extends AppCompatActivity implements CallBack{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memoryleak);

        ImageView imageView = findViewById(R.id.iv_memoryleak);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
        imageView.setImageBitmap(bitmap);

        CallBackManager.addCallBack(this);

        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .penaltyLog()
                .build());

        findViewById(R.id.iv_memoryleak).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeToExternalStorage();
            }
        });
    }

    /**
     * 文件系統的操作
     */
    public void writeToExternalStorage() {
        try {
            File externalStorage = Environment.getExternalStorageDirectory();

            File mbFile = new File(externalStorage, "xxx.txt");
            if (mbFile.exists()){
                mbFile.createNewFile();
            }

            OutputStream output = new FileOutputStream(mbFile, true);
            output.write("www.wooyun.org".getBytes());
            output.flush();
            output.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        CallBackManager.removeCallBack(this);
    }

    @Override
    public void dpOperate() {
        // do sth
    }
}

9125154-0c4edef63cd93d55.png
9125154-5db7bd056f34e351.png
Dialog的響應方式:
9125154-af155531b2cf1c33.png

以上IO違例的原因就是在主線程做了IO操作了
這顯然是不行的,需要開一個子線程給它整!

  • 實例限制檢測:
    9125154-c7731132212b0b3c.png
public class TestApp extends Application {

    static MemoryLeakActivity i = new MemoryLeakActivity();
    static MemoryLeakActivity j = new MemoryLeakActivity();

    @Override
    public void onCreate() {
        super.onCreate();

        //實例限制檢測 測試
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .setClassInstanceLimit(MemoryLeakActivity.class, 1)
                .detectLeakedClosableObjects() //API等級11
                .penaltyLog()
                .build());
    }
}










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章