看了之前前2篇博客,发现排版有问题,现在不管了,有时间在看下排版,现在继续把这篇写完,例外一周都尽力更新1—3篇博客,给自己的学习历程留下脚步。
前篇中单例模式中和观察者模式中发生的泄露其实就是static变量引起的。这篇介绍下非静态发生的泄露。如下:
1、非静态内部类引起泄露
在mainactivity中有如下的内部类
<pre name="code" class="java"> public void loadData(){//隐士持有MainActivity实例。MainActivity.this.a
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
//int b=a;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
内部类都隐式的持有了外部类的引用,因为在内部类中可以直接拿到外部类的成员变量。
分析:在activty中关闭后,如果run里面有网络操作,这时候用户不想等待直接退出activity,就造成了内存泄露。直到请求结束,方法执行完,GC才可以回收内存。
解决:把方法定义成static,里面的内部类也就是static了,静态的不属于任何对象的,只属于应用程序的,这时候就不会持有外部类的引用的。要想调用外部类的实例,那就要弱引用。
2、内部类Timer的使用
// new Timer().schedule(new TimerTask() {
// @Override
// public void run() {
// while(true){
// try {
// //int b=a;
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }
// }, 20000);//这个线程延迟5分钟执行
解决方案:activity onDestroy把timer.cancel掉然后赋空
3、Handler的使用不当
// private Handler mHandler = new Handler(){
// @Override
// public void handleMessage(Message msg) {
// super.handleMessage(msg);
// switch (msg.what){
// case 0:
// //加载数据
// break;
//
// }
// }
// };
分析:mHandler是匿名内部类的实例,会引用外部对象MainActivity.this。如果Handler在Activity退出的时候,它可能还活着,这时候就会一直持有Activity
解决方案:代码如下
private static class MyHandler extends Handler{
// private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露
private WeakReference<MainActivity> mainActivity;//设置软引用保存,当内存一发生GC的时候就会回收。
public MyHandler(MainActivity mainActivity) {
this.mainActivity = new WeakReference<MainActivity>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity main = mainActivity.get();
if(main==null||main.isFinishing()){
return;
}
switch (msg.what){
case 0:
//加载数据
// MainActivity.this.a;//有时候确实会有这样的需求:需要引用外部类的资源。怎么办?
int b = main.a;
break;
}
}
};
这里有人会想,这里用弱引用,那不是GC会随时干掉Activity嘛? 其实,你想,GC会干掉一个正在呈现的ativity嘛?肯定不会。如果其他引用这个activity的实例没有问题的话,那当activity退出的时候,就会随时被干掉了。
有时候在轮播图的用到Handler时,记得在onDestory中remove掉。
4、getViewTreeObserver()的使用
有时候需要在onCreate方法中知道某个View组件的宽度和高度等信息,而直接调用View组件的getWidth()、getHeight()、getMeasuredWidth()、getMeasuredHeight()、getTop()、getLeft()等方法是无法获取到真实值的,只会得到0。这是因为View组件布局要在onResume回调后完成。这时候getViewTreeObserver()就派上用场了
mGridView.getViewTreeObserver().addOnGlobalLayoutListener( //view 布局完成时调用,每次view改变时都会调用
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mAdapter.getNumColumns() == 0) {
final int numColumns = (int) Math.floor(
mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));
if (numColumns > 0) {
final int columnWidth =
(mGridView.getWidth() / numColumns) - mImageThumbSpacing;
mAdapter.setNumColumns(numColumns); //设置 列数
mAdapter.setItemHeight(columnWidth); //设置 高度
}
}
}
});
在gridview布局完成后,根据girdview的宽和高设置adapter列数和每个item高度,但是需要注意的是OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。即在onGlobaLayout()中调用getViewTreeObserver() .removeGlobalOnLayoutListener(this);
结论:像传感器,广播,服务,之类,有注册添加的,在activity退出的时候就要考虑是否要取消注册或者remove掉,动画的时候
完结此篇,废话,废话,还是不说废话了,再接再厉!