1.内存泄露问题对一个app的使用影响还是挺大的,建议重点关注。
2.今天再看个非静态内部类或者匿名内部类导致的内存泄露问题。
(1)app从A页面跳转到B页面。在B页面有下面这样一段代码:
private Handler mhandle = new Handler() {
@Override
public void handleMessage(final Message msg) {
runOnUiThread(new Runnable() { //主线程刷新
@Override
public void run() {
String obj = (String) msg.obj;
tvData.setText(obj);
}
});
}
};
private void sendMsg() {
tvSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = new Message();
message.obj = "datass";
mhandle.handleMessage(message);
}
}).start();
}
});
}
大致意思就是,B页面上有个按钮,点击发送handle消息,并且,在当前页面刷新Ui,设置发送的数据到一个textview里面。
A页面的跳转:
tvGo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,Main2Activity.class));
}
});
(2)需要注意2点:第一:BActivity的mhandle是采用内部类的方式创建的,第二:发送handle消息是在子线程操作的,并且模拟了,耗时操作6s。6s后,发送消息,更新Ui。
3.上面的代码,在A页面点击跳转B页面,B页面按返回键,返回到B页面,来回切换2次以上,B页面的实例会越来越多,也就是说,B页面activity泄露了,资源没有全部释放。检测警告如下:
11-26 15:59:03.939 14797-14797/com.example.xyc.memoryleakapp E/StrictMode: class com.example.xyc.memoryleakapp.Main2Activity; instances=2; limit=1
android.os.StrictMode$InstanceCountViolation: class com.example.xyc.memoryleakapp.Main2Activity; instances=2; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
11-26 15:59:07.056 14797-14797/com.example.xyc.memoryleakapp E/StrictMode: class com.example.xyc.memoryleakapp.Main2Activity; instances=3; limit=1
android.os.StrictMode$InstanceCountViolation: class com.example.xyc.memoryleakapp.Main2Activity; instances=3; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
11-26 15:59:08.708 14797-14797/com.example.xyc.memoryleakapp E/StrictMode: class com.example.xyc.memoryleakapp.Main2Activity; instances=4; limit=1
android.os.StrictMode$InstanceCountViolation: class com.example.xyc.memoryleakapp.Main2Activity; instances=4; limit=1
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
4.这里需要注意的是,泄露原因是因为,Looper的MessageQueue里面还有未处理的消息Message,而Message又引用了mhandle,如果mhandle采用匿名内部类的方式创建,又会默认持有activity,所以消息未处理完,activity也释放不了。但是,当消息队列执行完(B页面执行6s后),当前activity是会自动回收掉的,资源可以被回收,如果这个消息是持久循环 发送就另说了。
5.解决办法:
方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
方法二:将Handler声明为静态类。
PS:在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。
static class MyHandler extends Handler
{
WeakReference<Activity> mWeakReference;
public MyHandler(Main2Activity activity)
{
mWeakReference=new WeakReference< Main2Activity >(activity);
}
@Override
public void handleMessage(Message msg)
{
final Activity activity=mWeakReference.get();
if(activity!=null)
{
if (msg.what == 1)
{
noteBookAdapter.notifyDataSetChanged();
}
}
}
}