Android中一個簡單有用的發現性能問題的方法

lbei2111 7年前發布 | 5K 次閱讀 安卓開發 Android開發 移動開發

在Android中,性能優化是我們持之不懈的工作。這其中,在主線程執行耗時的任務,可能會導致界面卡頓,甚至是ANR(程序未響應)。當然Android提供了很多優秀的工具,比如StrictMode,Method Tracing等,便于我們檢測問題。

這里,本文將介紹一個更加簡單有效的方法。相比StrictMode來說更加便于發現問題,相比Method Tracing來說更加容易操作。

首先,我們有這樣一個程序代碼

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    writeContentToFile();
}

private void writeContentToFile() {
    File log = new File(Environment.getExternalStorageDirectory(), "Log.txt");
    Writer outWriter = null;
    try {
        outWriter = new BufferedWriter(new FileWriter(log.getAbsolutePath(), false));
        outWriter.write(new Date().toString());
        outWriter.write(" : \n");
        outWriter.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (outWriter != null) {
            try {
                outWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代碼需要優化,因為

  • writeContentToFile 是一個本地文件寫操作,比較耗時
  • 而writeContentToFile 這個方法卻放在了主線程中,必然會阻塞主線程其他的工作順利執行。

上面介紹StrictMode和Method Traing都可以檢測這個問題,這里我們我們用一個更簡單的方法

public void checkWorkerThread() {
    boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
    if (isMainThread) {
        if (BuildConfig.DEBUG) {
            throw new RuntimeException("Do not do time-consuming work in the Main thread");
        }
    }
}

這段方法有幾點注意的。

  • 主線程判斷,使用 Looper.myLooper() == Looper.getMainLooper() 可以準確判斷當前線程是否為主線程。
  • BuildConfig.DEBUG 條件控制,只有在debug環境下拋出異常,給予開發者明顯的提示。當然也可以使用自定義的是否拋出異常的邏輯
  • 如果當前線程不是主線程,那么就被認為是工作者線程。

比如上面的方法加入checkWorkerThread檢查

private void writeContentToFile() {
    checkWorkerThread();
    //代碼省略,具體實現參考上面
}

再次執行程序,會曝出異常。

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.droidyue.checkthreadsample/com.droidyue.checkthreadsample.MainActivity}: java.lang.RuntimeException: Do not do time-consuming work in the Main thread
          at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2664)
          at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2733)
          at android.app.ActivityThread.access$900(ActivityThread.java:187)
          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1584)
          at android.os.Handler.dispatchMessage(Handler.java:111)
          at android.os.Looper.loop(Looper.java:194)
          at android.app.ActivityThread.main(ActivityThread.java:5869)
          at java.lang.reflect.Method.invoke(Native Method)
          at java.lang.reflect.Method.invoke(Method.java:372)
          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1019)
          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:814)
 Caused by: java.lang.RuntimeException: Do not do time-consuming work in the Main thread
          at com.droidyue.checkthreadsample.MainActivity.checkWorkerThread(MainActivity.java:34)
          at com.droidyue.checkthreadsample.MainActivity.writeContentToFile(MainActivity.java:40)
          at com.droidyue.checkthreadsample.MainActivity.onCreate(MainActivity.java:27)
          at android.app.Activity.performCreate(Activity.java:6127)
          at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
          at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2617)
          ... 10 more

通過分析crash stacktrace 我們可以很輕松的發現問題的根源并解決。

哪些方法需要加上檢查

  • 本地IO讀寫
  • 網絡操作
  • Bitmap相關的縮放等
  • 其他耗時的任務

如何選擇工作者線程

Android中的工作者線程API有很多,簡單的有Thread,AsyncTask,也有ThreadPool,HandlerThread等。

對比

  • StrictMode 是一把利器,但是檢測的東西很多,打印出來的日志可能也有很多,查找定位問題可能不如文章的方法方便。
  • Method Tracing,需要刻意并時不時進行設置start和stop操作,文章的方法,可以說是一勞永逸。

檢測會不會有性能問題

  • 理論上是不會的,通常這個檢測的代價要遠遠比耗時任務要小很多。
  • 如果想進一步優化的,可以在編譯期屏蔽這個方法的調用,即assumenosideeffects。

 

 

來自:http://droidyue.com/blog/2017/03/13/a-small-trick-to-detect-time-consuming-task/

 

 本文由用戶 lbei2111 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!