Android WeakReference 带来的问题
背景
其实开始的时候本人也像很多人一样,因为Handler出现的内存泄露问题,因此开始使用起weakReference的,还信心满满的以为,从此我和我的小伙伴再也不担心内存泄露。
然而,其实weakReference真的那么好?来看代码吧,我们常规时候定义一个自定义的Handler来异步请求处理代码,当然LZ表示自己是个很懒得人,因此自己写了个BaseHandler通过反射直接执行回调方法,这个先不说。
public static class MyHandler extends Handler{
private WeakReference<MyActivity> wActivity;
public MyHandler(MyActivity activity){
wActivity = new WeakReference(activity);
}
void handleMessage(Message msg){
//do something
MyActivity myActivity = wActivity .get();
if(myActivity !=null){
myActivity.doSomething();
}
}
}
这是我们常规写的,那么有啥问题?这似乎木有问题啊,恩,LZ当初也是这样想的,直到一天我手贱的时候。 大家都知道,Java垃圾回收机制对吧。知道的话应该知道LZ接下来说的问题,对当内存不足的时候WeakReference会被回收的,但是有个问题是当前Activity还在活动的时候WeakReference是否还是会被回收? ...答案肯定的 一样回收!!!。因此你想过木有突然出发内存回收,那么你本来要回调的方法,呵呵呵呵呵呵.....那么怎么会触发他回收,我做的很简单,那时因为Activity前面已经有很长的Activity栈,而且LZ很无耻的不停来回切换一个不是singleTask或者singleTop的Activity。然后某一次,有个回调方法没调用....看如下测试代码(这个是我之前写一篇文章 Android MVVM使用经验篇中一段代码):
public class AdapterModule<T> {
// not use onClick
public static final int BINDVAIABLE_NONE = 0;
public ObservableArrayList<T> list = new ObservableArrayList<>();
// bindingVaiable id
public ObservableInt bindingVaiable;
// bind PositionId
public ObservableInt bindPositionVaiableId;
// the key is the resourceId!
public WeakReference<SparseArray<OnClickListener>> listeners;
// manual notify
public boolean manualNotify = false;
//代码省略
}
@Override
public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (inflater == null) {
inflater = LayoutInflater.from(parent.getContext());
}
ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutId.get(), null, false);
final BindingHolder holder = new BindingHolder(binding, (SparseArray<OnClickListener>) adapterModule.listeners.get());
///代码省略
return holder;
}
public static class BindingHolder<T> extends RecyclerView.ViewHolder {
ViewDataBinding binding;
private SparseArray<OnClickListener> listeners;
public BindingHolder(ViewDataBinding binding, SparseArray<OnClickListener> listeners) {
super(binding.getRoot());
this.binding = binding;
this.listeners = listeners;
Log.e("textListener is null?", (listeners == null) + "");
onBindListeners();
}
public void onBindListeners() {
if (listeners != null && listeners.size() > 0) {
for (int i = 0; i < listeners.size(); i++) {
binding.setVariable(listeners.keyAt(i), listeners.get(listeners.keyAt(i)));
}
}
}
}
最后在我来回切换页面时候有以下输出:
....前面省略N个。
09-28 21:48:39.859 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:39.866 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:39.873 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:40.925 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:40.932 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:40.940 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:41.975 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:41.983 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:41.990 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:42.958 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:42.964 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:42.970 14872-14872/com.ykse.ticket E/textListener is null?: false
09-28 21:48:44.074 14872-14872/com.ykse.ticket E/textListener is null?: true
09-28 21:48:44.081 14872-14872/com.ykse.ticket E/textListener is null?: true
09-28 21:48:44.091 14872-14872/com.ykse.ticket E/textListener is null?: true
看到木有-。-或许前面有N个没事,但是某一个时刻,哼哼,说了那么多怎么解决,其实也很简单。。。不用WeakReference呗,然后在相关Activity或者Fragment或者View detach时候将Activity引用去掉就行了呗~当然如果你非要说道线程不安全-。-要加同步锁,我不反对~~看代码
public static class MyHandler extends Handler{
private MyActivity activity;
public MyHandler(MyActivity activity){
this.activity= activity;
}
void handleMessage(Message msg){
//do something
if(myActivity !=null){
myActivity.doSomething();
}
}
public synchronized void destory(){
myActivity = null;
}
}