我们先创建一个内存泄露的例子
首先,创建一个简单Android Java工程,里面有MainActivity和MainActivity2,其中MainActivity2用来模拟Handler的内存和单例内存泄露例子
MainActivity 跳转到MainActivity2
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_go).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplication(),MainActivity2.class);
intent.putExtra("hhh","hhhh");
startActivity(intent);
}
});
}
}
MainActivity2模拟了handler和单例持有上下文的内存泄露
public class MainActivity2 extends AppCompatActivity {
//定义Handler对象
private Handler handler = new Handler() {
@Override
//当有消息发送出来的时候就执行Handler的这个方法
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(MainActivity2.this, "数据返回了!", Toast.LENGTH_LONG).show();
Log.i("MainActivity2", "handleMessage -->" + Thread.currentThread().getName());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
//创建单例,引用活动上下文会发现内存泄露
TestFactory factory = TestFactory.getInstance(MainActivity2.this);
//handler内存泄露测试
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
processThread();
}
});
}
private void processThread() {
//构建一个下载进度条
Log.i("MainActivity2", "processThread()-->" + Thread.currentThread().getName());
new Thread() {
@Override
public void run() {
Log.i("MainActivity2", "run()-->" + Thread.currentThread().getName());
//在新线程里执行长耗时方法
longTimeMethod();
//执行完毕后给handler发送一个空消息
handler.sendEmptyMessage(0);
}
}.start();
}
//模拟下载文件的长耗时方法
private void longTimeMethod() {
try {
Log.i("MainActivity2", "longTimeMethod-->" + Thread.currentThread().getName());
Thread.sleep(10000); //10秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
单例类
public class TestFactory {
private Context context;
private static volatile TestFactory testFactory;
public TestFactory(Context context) {
this.context = context;
}
public static TestFactory getInstance(Context context) {
if (testFactory == null) {
synchronized (TestFactory.class) {
if (testFactory == null) {
testFactory = new TestFactory(context);
}
}
}
return testFactory;
}
}
启动项目,打开Profiler,绑定我们的进程,进入内存分析视图,具体使用方法可以参考上一章
内存泄露的自动分析需要使用第一种Captrue heap dump Captrue heap dump
每点击一次Record,都会记录当前App内存情况,生成一个Heap Dump记录,所以app操作之后点击一次Record,内存情况记录会跟上一次有所不同的。 GC按钮图标是一个垃圾桶:
打开内存监控就可以进行操作了 点击Mainctivity的跳转按钮,跳转到Mainctivity2 , 然后点击执行执行processThread函数的方法,这边可以自己实现,退出HMainctivity2,回到MainActivity。然后回到Android Profile,执行一次GC按钮(模拟GC回收不可达的引用),再点击Record按钮,就会得到一个Heap Dump "记录1"
我们看到,Leaks 数量是2,说明两种情况都内存泄露了
按照箭头的顺序点击便可以查看到泄露的对象
可以看出因为单例持有了活动上下文导致无法被销毁
第二个泄露是hander的这里就不展开分析了
细心的网友可能会问,除了第一个选项,后面两个做什么的呢? 根据注释说明我们可以知道主要分析native和kottlin的内存使用情况,如果用这个查内存泄露需要自己观察数据,不是最优选择
是不是很简单,你学废了吗?