LeakCanary:LeakCanary是开源大户Square的一款开源产品,用于检测Android 和 Java 程序中的内存泄露。
1. 配置与使用
(1) 在 build.gradle 中加入引用
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
}
(2) 自定义Application中并配置
package comi.example.liy.mytestdemo;
import android.app.Application;
import android.content.Context;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
/**
* Created by liy on 2019-11-15 14:33
* 检测内存泄漏:如果检测到activity或Fragment有内存泄露,LeakCanary 就会自动地显示一个通知
* 监控activity:LeakCanary在调用install方法时会自动启动一个ActivityRefWatcher类(用于自动监控Activity执行onDestroy方法之后是否发生内存泄露),所以在activity不需再手动添加RefWatcher类来监控是否发生内存泄露
* 说明:除Activity类外的其他类的内存泄漏,需要使用RefWatcher来进行监控
* 监控Fragment:需在Fragment中的onDestroy方法中手动添加RefWatcher类来监控是否发生内存泄露
*/
public class ExampleApplication extends Application {
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);//(1)LeakCanary做初始化:install方法会返回RefWatcher用来监控对象
}
//(2)提供getRefWatcher静态方法来返回全局RefWatcher:使用RefWatcher监控那些本该被回收的对象
public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}
}
注意:别忘了在manifest.xml中注册自定义的Application
注册自定义的Application.png
(3) 监控Activity
/**
* Created by liy on 2019-11-15 9:01
*/
public class MemoryLeakTestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onDestroy() {
super.onDestroy();
//检测内存泄漏:在activity不需手动添加RefWatcher类来监控是否发生内存泄露(如果检测到activity有内存泄露,LeakCanary 就会自动地显示一个通知)
/*RefWatcher refWatcher = ExampleApplication.getRefWatcher(this);
refWatcher.watch(this);*/
}
}
(4) 监控Fragment
package comi.example.liy.mytestdemo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import com.squareup.leakcanary.RefWatcher;
/**
* Created by liy on 2019-11-15 15:20
*/
public class MemoryLeakTestFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDestroy() {
super.onDestroy();
//检测内存泄漏:需在Fragment中的onDestroy方法中手动添加RefWatcher类来监控是否发生内存泄露
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}
2. 完整示例
(1) 在Activity中检测内存泄漏
package comi.example.liy.mytestdemo;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import java.lang.ref.WeakReference;
/**
* Created by liy on 2019-11-15 9:01
*/
public class MemoryLeakTestActivity extends AppCompatActivity {
//private MemoryLeakTestActivity activity;
private TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//activity = this;
textView = findViewById(R.id.text);
/**
* 1.单例模式造成的内存泄漏
*/
UserManager userManager = UserManager.getInstance(this);
LoadData();
}
/**
* 2.非静态匿名内部类的静态实例造成的内存泄漏
* 内存泄漏原因分析:匿名内部类Runnable持有对当前Activity的隐式引用
* 当Activity销毁的时候(如返回键),若Runable内部的任务还未完成,那么将导致Activity的内存资源无法回收,造成内存泄漏。
*/
private void LoadData(){
/*new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.v("LoadData","非静态匿名内部类的静态实例造成的内存泄漏");
}
}).start();*/
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
}
private static class MyRunnable implements Runnable{//自定义静态内部类MyRunnable并实现Runnable
@Override
public void run() {
SystemClock.sleep(2000);
Log.v("LoadData","成功解决非静态匿名内部类的静态实例造成的内存泄漏问题");
}
}
/**
* 3.非静态内部类的静态实例造成的内存泄漏
* 内部类:当外部类activity启动时,都会使用内部类中的数据;虽然避免了资源的重复创建,但这种非静态内部类写法会造成内存泄漏
* 内存泄漏原因分析:在java中非静态内部类默认持有外部类的引用,
* 若在非静态内部类中创建静态实例,则该实例的生命周期和应用一样长,
* 导致该静态实例一直持有acticity,使activity无法被释放
* 解决内存泄漏:在java中静态内部类不会持有外部类的引用,故可将内部类设为静态内部类
*/
/*class Resource{//非静态内部类默认持有外部类的引用,导致内存泄漏问题
private static final String des1 = "";//在非静态内部类中创建静态实例,则该实例的生命周期和应用一样长,导致该静态实例一直持有acticity,使activity无法被释放
}*/
static class Resource{//静态内部类不会持有外部类的引用,避免了内存泄漏问题
private static final String des2 = "";
}
@Override
protected void onResume() {
super.onResume();
new MyThread().start();
}
private class MyThread extends Thread{
@Override
public void run() {
super.run();
try{
sleep(5000);
}catch (Exception e){
e.printStackTrace();
}
Message message = new Message();
message.what = 1;
myHandler.sendMessage(message);
}
}
/**
* 3.1 Handler造成的内存泄漏:myHandler其实是非静态内部类Handler的实例,所以它持有外部类acticity的引用
* 分析:myHandler和activity的生命周期不一样,当activity退出需释放activity时,如果消息队列中还有消息未处理,那么将导致myHandler所持有的activity无法被回收
*/
/*private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
//Toast.makeText(MemoryLeakTestActivity.this,"更新UI",Toast.LENGTH_SHORT).show();
Toast.makeText(activity,"更新UI",Toast.LENGTH_SHORT).show();
}
}
};*/
private MyHandler myHandler = new MyHandler(this);
private static class MyHandler extends Handler{//(1)静态内部类:自定义静态内部类MyHandler并继承Handler,然后在使用的时候实例化
private WeakReference<Context> reference;//(2)弱引用:MyHandler内部持有activity的弱引用
public MyHandler(Context context){
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MemoryLeakTestActivity activity = (MemoryLeakTestActivity) reference.get();
if (activity != null){
activity.textView.setText("成功解决Handler造成的内存泄漏问题");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//检测内存泄漏:在activity不需手动添加RefWatcher类来监控是否发生内存泄露(如果检测到activity有内存泄露,LeakCanary 就会自动地显示一个通知)
/*RefWatcher refWatcher = ExampleApplication.getRefWatcher(this);
refWatcher.watch(this);*/
myHandler.removeCallbacksAndMessages(null);//(3)资源回收:在activity的onDestory()生命周期函数中调用handler.removeCallbacks()方法
}
}
(2) 运行程序界面生成一个名为Leaks的应用图标
image.png
(3) 查看内存泄漏信息
若不断的切换横竖屏(会造成内存泄漏),这时会闪出一个提示框,提示内容为:“Dumping memory app will freeze.Brrrr.”,再稍等片刻,内存泄漏信息就会通过Notification展示出来,点击Notification就可以进入内存泄漏详细页;除此之外也可以通过Leaks应用的列表界面进入,如下图所示:
内存泄漏列表界面.png
内存泄漏详情界面.png
网友评论