背景
最近自己项目上用了网上的AutoScrollViewPager 开源Viewpager。没怎么关注别人开源的项目一些代码质量问题, 因为潜意识上别人开源在网上的肯定是令人满意的代码。最近意识到了这种心态其实并不好,而且不利于自身技术的提升。
样例
贴这个View的内存泄漏的样例,是因为发现很多程序员们在使用一些第三方开源框架,特别是一些自定义view的特效时,会用到一些延时任务之类的,那就会用到Handler这个类。在Android中,典型的Context+Handler造成的内存泄漏现象也是常见的。
来看下没有处理过之前得AutoScrollViewPager 中Handler的代码:
private class H extends Handler {
@Override
public void handleMessage(Message msg) {
Context context = getContext();
switch (msg.what) {
case MSG_AUTO_SCROLL:
setCurrentItem(getCurrentItem() + 1);
sendEmptyMessageDelayed(MSG_AUTO_SCROLL, intervalInMillis);
break;
default:
super.handleMessage(msg);
break;
}
}
}
问题:当程序进入handlerMessage的时候,可能context所在的Activity,Fragment已经关闭,那么因为Handler的存在,因为Handler new出来后是和当前线程绑定在一起的, 不明白的同学可以去看看Handler,MessageQuee,Looper相关源码。所以Handler在执行handlerMessage里因为持有当前自定义view的context,导致context在Activity,Fragment关闭时候无法正常释放,造成context内存泄漏。
来看下Memory Usage在Activity关闭后给出的结果:
Objects:
Views: 16 ViewRootImpl: 1
AppContexts: 4 Activities: 1
Assets: 6 AssetManagers: 6
Local Binders: 15 Proxy Binders: 20
Parcel memory: 5 Parcel count: 23
Death Recipients: 1 OpenSSL Sockets: 0
可以看到Activity依然存活。
正确修改后:
private static class H extends Handler {
private AutoScrollViewPager autoScrollViewPager;
private WeakReference<AutoScrollViewPager> viewHolder;
public H(AutoScrollViewPager autoScrollViewPager){
this.autoScrollViewPager = autoScrollViewPager;
viewHolder = new WeakReference<AutoScrollViewPager>(autoScrollViewPager);
}
@Override
public void handleMessage(Message msg) {
if (viewHolder.get()!=null){
switch (msg.what) {
case MSG_AUTO_SCROLL:
autoScrollViewPager.setCurrentItem(autoScrollViewPager.getCurrentItem() + 1);
sendEmptyMessageDelayed(MSG_AUTO_SCROLL, autoScrollViewPager.intervalInMillis);
break;
default:
super.handleMessage(msg);
break;
}
}
}
}
这里做了几步:
1.将Handler声明为static
2.因为在handlerMessage中无法访问外部非静态方法,所以传入autoviewpager作为成员变量
3.使用WeakReference包裹autoviewpager(context) 使程序执行完handlerMessage之后的GC回收时回收autoviewpager中的context。
看现在的Memory Usage结果:
Objects
Views: 0 ViewRootImpl: 0
AppContexts: 3 Activities: 0
Assets: 6 AssetManagers: 6
Local Binders: 13 Proxy Binders: 19
Parcel memory: 5 Parcel count: 22
Death Recipients: 1 OpenSSL Sockets: 0
分享结束,告诫自己,使用handler注意内存泄漏问题,还Android一个绿色环境。