Android高级进阶知识android移动端专项目测试

安卓使用LeakCanary检测代码内存泄漏和BlockCan

2016-08-31  本文已影响1078人  矢准Plit

使用LeakCanary检测代码的内层泄漏

首先我们看下面的代码
public class MainActivity extends AppCompatActivity {    
     private Button btn_load;   
     private Handler mHandler = new Handler() {        
            @Override        
            public void handleMessage(Message msg) {    
                   if(msg.what == 0) {                                                    
                      Log.i("handleMessage", "got datas");    
                   }  
             }  
      };    
      @Override    
      protected void onCreate(Bundle savedInstanceState) {                               
                super.onCreate(savedInstanceState); 
                setContentView(R.layout.activity_main);        
                btn_load = (Button)findViewById(R.id.btn_load);
                btn_load.setOnClickListener(new View.OnClickListener() {    
                         Override    
                         public void onClick(View v) { 
                                Log.i("btn_load", "loading datas"); 
                                loadData(); 
                         }
                });
       private void loadData() {    
               new Thread(new Runnable() {        
                   @Override        
                  public void run() {            
                         //do sonething            
                         SystemClock.sleep(10000);            
                        //发送消息            
                       mHandler.sendEmptyMessageDelayed(0, 20000);      
                  }    
              }).start();}
MainActivity泄漏流程
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
public class MyApp extends Application {   
              @Override   
              public void onCreate() {        
                       super.onCreate();      
                       LeakCanary.install(this);    
              }
}
private static class  MyThread extends  Thread {    
          private WeakReference<MainActivity> weak;        
          public MyThread(MainActivity activity) {        
                 weak = new WeakReference<MainActivity>(activity);   
          }    
          @Override    
          public void run() {        
                 //do sonething        
                 SystemClock.sleep(100);        
                //发送消息       
                if(null != weak && null != weak.get()) { 
                    weak.get().mHandler.sendEmptyMessageDelayed(0, 20000);       
                }
          }                
} 

loadData()中修改如下

new MyThread(this).start();

开启界面后, 立即关闭,等待一段时间后,又出现泄漏,检查LeakCanary,获取以下的结果:


MainActivity泄漏流程

究其原因是和上述线程是一样的,只不过这次泄漏的是Handler对象。
所以,我们再定义一个Handler的静态内部类,代码如下:

private static class  MyHandler extends Handler {    
          private WeakReference<MainActivity> weak;    
          public MyHandler(MainActivity activity) {        
                 weak = new WeakReference<MainActivity>(activity);   
          }    
          @Override    
          public void handleMessage(Message msg) {       
                 if(msg.what == 0) {            
                    Log.i("handleMessage", "got datas");            
                   if(null != weak && null != weak.get()) {    
                        weak.get().textView.setText("goodbye world");           
                   }       
                 }  
          }
}

再次运行程序将不会产生泄漏问题

@Override
protected void onDestroy() {    
         super.onDestroy();    
         mHandler.removeCallbacksAndMessages(null);
}
private MyThread mt;
private boolean isClose;

提供的方法

public void close() {    
       if(null != weak && null != weak.get()) {      
              weak.get().isClose = true;    
       }
}

修改run() 的逻辑

if(null != weak && null != weak.get()) {  
      if(weak.get().isClose) {        
          //直接返回      
           return;   
       }
}

onDestroy()调用

mt.close();
 private WeakReference<MainActivity> weak; 

其作用是为了然我们的静态内部类可以调用外部类的非静态的字段和方法,从而只有一个外部类对象的引用,但这样做就又回到导致我们的代码泄漏的最初的原因,怎么办呢,于是弱引用横空出世了。弱引用的特点是一旦被gc扫描到就会被立即回收,而不管是否被引用,这也是为什么每次我们使用时都要判断其是否为null的原因。与之对应的还有软引用(SoftReference), 强引用, 虚引用, 相关的详细说明大家自行搜索啊。


使用BlockCanary优化代码的结构

compile 'com.github.moduth:blockcanary-android:1.2.1'
// 仅在debug包启用BlockCanary进行卡顿监控和提示的话,可以这么用 
debugCompile 'com.github.moduth:blockcanary-android:1.2.1' 
   releaseCompile 'com.github.moduth:blockcanary-no-op:1.2.1'
public class AppBlockCanaryContext extends BlockCanaryContext {    
          // override to provide context like app qualifier, uid,     network type, block threshold, log save path    
          // this is default block threshold, you can set it by phone's performance    
          @Override    
          public int getConfigBlockThreshold() {       
                return 500;   
          }    
          // if set true, notification will be shown, else only write log file 
          @Override   
          public boolean isNeedDisplay() {       
             return BuildConfig.DEBUG;  
          }   
         // path to save log file (在SD卡目录下)   
        @Override   
         public String getLogPath() {     
             return "/blockcanary/performance";   
         }
}
public class MyApp extends Application {    
      @Override    
      public void onCreate() {        
           super.onCreate();       
           BlockCanary.install(this, new AppBlockCanaryContext()).start();  
      }
}  
卡顿检测结果图

结果显示第34行有卡顿情况,我们找到这一行:


卡顿的地方

我们还可以查看更详细的信息

卡顿的详细信息

获取到卡顿的代码位置,我们就可以着手修改代码和重构了



上一篇下一篇

猜你喜欢

热点阅读