1. 程式人生 > >Android記憶體洩露分析之StrictMode

Android記憶體洩露分析之StrictMode

轉載請註明地址:http://blog.csdn.net/yincheng886337/article/details/50524709

StrictMode(嚴格模式)使用

StrictMode嚴格模式,主要用來檢測程式中違例情況的開發者工具。最常用的場景就是檢測主執行緒中本地磁碟、網路讀寫等耗時的操作以及Activity洩露等,但該模式不建議在Release版本開啟,此外該模式無法監控JNI中的磁碟IO和網路請求且其違例情況僅供參考,需結合實際開發需求予以解決。

StrictMode檢測什麼?

主要採用採用ThreadPolicy(執行緒策略)和VmPolicy(Vm策略)進行檢測,各策略檢測內容如下:

ThreadPolicy

執行緒策略檢測的內容有

· 自定義的耗時呼叫 使用 detectCustomSlowCalls() 開啟

· 磁碟讀取操作 使用 detectDiskReads() 開啟

· 磁碟寫入操作 使用 detectDiskWrites() 開啟

· 網路操作 使用 detectNetwork() 開啟

VmPolicy

虛擬機器策略檢測的內容有

· Activity洩露 使用 detectActivityLeaks() 開啟

· 未關閉的Closable物件洩露 使用 detectLeakedClosableObjects() 開啟

· 洩露的Sqlite物件 使用 detectLeakedSqlLiteObjects() 開啟

· 檢測例項數量 使用 setClassInstanceLimit() 開啟

StrictMode具體使用

<span style="font-family:FangSong_GB2312;font-size:18px;">public class DebugUtil {
    public static void startStrictModeVmPolicy(){
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
        .detectActivityLeaks()/*檢測Activity記憶體洩露*/
        .detectLeakedClosableObjects()/*檢測未關閉的Closable物件*/
        .detectLeakedSqlLiteObjects() /*檢測Sqlite物件是否關閉*/   
        /*也可以採用detectAll()來檢測所有想檢測的東西*/
        .penaltyLog().build());
    }
    public static void startStrictModeThreadPolicy(){
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()/*磁碟讀取操作檢測*/
        .detectDiskWrites()/*檢測磁碟寫入操作*/
        .detectNetwork() /*檢測網路操作*/   
        /*也可以採用detectAll()來檢測所有想檢測的東西*/
        .penaltyLog().build());
}
}</span>

如果你想檢測整個App或某Activity的相關洩露問題,可在Application或Activity的onCreate方法中直接呼叫DebugUtil中封裝好的策略即可

如何檢視檢測結果?

只需要檢視TAG為StrictMode的日誌即可,如:logcat -c;logcat -s StrictMode 或者adb logcat | grep StrictMode

如何解決檢測出來的問題?

針對此問題下面給出幾點建議,僅供參考:

1.如果是主執行緒中出現檔案讀寫違例問題,建議使用工作執行緒(可採用HandlerThread,IntentService、執行緒池或直接new Thread,必要時可結合Handler)完成,但採用工作執行緒在某個Activity中操作時注意執行緒要能正常結束,否則將導致記憶體洩露,相關細節後文將有表述。

2.如果是對SharedPrefrences寫入操作,在API 9以上建議優先呼叫apply而非commit,此外需注意的是確保SharedPrefrences在單程序中使用,如果涉及跨程序資料交換,建議自己編寫跨程序SharedPrefrences實現機制,否則可能導致資料不準確。

3.如果存在未關閉的Closable物件,需根據對應的stacktrace進行關閉。

4.如果SQLite物件洩露,根據對應的stacktrace進行釋放。

5.注意registerBroadcast和unregisterBroadcast配對使用,對檔案操作完成後記得close操作。

StrictMode使用示例

現以主執行緒中檔案讀寫為例,引起違例警告的程式碼如下:

<span style="font-family:FangSong_GB2312;font-size:18px;">public void writeToExternalStorage() {
        File externalStorage = Environment.getExternalStorageDirectory();
        File destFile = new File(externalStorage, "dest.txt");
        try {
          OutputStream output = new FileOutputStream(destFile, true);
            output.write("droidyue.com".getBytes());
            output.flush();
            output.close();
        } catch (FileNotFoundException e) {
              e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        }
    }</span>

StrictMode違例警告:

<span style="font-family:FangSong_GB2312;font-size:18px;">D/StrictMode( 9730): StrictMode policy violation; ~duration=20 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=31 violation=2
            D/StrictMode( 9730):    at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1176)
            D/StrictMode( 9730):    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:106)
            D/StrictMode( 9730):    at libcore.io.IoBridge.open(IoBridge.java:390)
            D/StrictMode( 9730):    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
            D/StrictMode( 9730):    at com.example.strictmodedemo.MainActivity.writeToExternalStorage(MainActivity.java:56)
            D/StrictMode( 9730):    at com.example.strictmodedemo.MainActivity.onCreate(MainActivity.java:30)
            D/StrictMode( 9730):    at android.app.Activity.performCreate(Activity.java:4543)</span>

解決方法:

<span style="font-family:FangSong_GB2312;font-size:18px;">public void writeToExternalStorage() {
        new Thread(){
            @Override
            public void run() {
                /*將對讀寫操作移至執行緒*/
                File externalStorage = Environment.getExternalStorageDirectory();
                File destFile = new File(externalStorage, "dest.txt");
                OutputStream output = null;
                try {
                    output = new FileOutputStream(destFile, true);
                    output.write("droidyue.com".getBytes());
                    output.flush();                    
                } catch (FileNotFoundException e) {
                      e.printStackTrace();
                } catch (IOException e) {
                  e.printStackTrace();
                }finally{
                    /*對檔案操作完成後,注意關閉*/
                    if(output != null){
                        output.close();
                    }
                }
            }
        }        
    }</span>