最近做一个需求,需要通过view.getContext()来获取activity,按理说,这是一个很正常的操作,但是没想到有一次获取到了application,与相关开发沟通后,得到的结论是为了防止内存泄露。
但我并不这么认为,我觉得只要这个view有添加在持有activity的viewgroup中,如果view本身无法释放,view中的parent也无法被释放,也就会导致activity内存泄露,所以与是否传入application无关。
由此去翻了资料。查到WindowManager会在Activity调用onDestroy之后,调用removeViewImmediate方法。
代码如下:
经过断点调试后发现,removeViewLocked()方法中的view.assignParent(null)的view,其实只有DecorView,这其实只是WindowManager和Activity解除绑定。具体到单个子View,并没有调用到view.assignParent(null)这个方法,所以结论依然是:
子View继续持有parent,parent也持有activity,所以activity还是无法被释放。
为此专门做了个实验,代码如下:
public class LeakActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
findViewById(R.id.add_leak_view_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LeakView leakView = new LeakView(v.getContext().getApplicationContext());
ViewGroup group = findViewById(R.id.viewgroup);
group.addView(leakView);
leakView.leak();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
ViewGroup group = findViewById(R.id.viewgroup);
group.removeAllViews();
}
static class LeakView extends AppCompatTextView {
public LeakView(Context context) {
super(context);
setText("我是内存泄露View");
}
public void leak() {
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (true) {
i++;
System.out.println("我是内存泄露线程,啦啦啦啦" + i + ",parent=" + getParent()+","+getContext());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "leak thread").start();
}
}
}
实验结果:
- LeakView传入的是LeakActivity,则不管有没有添加到viewgroup,activity和LeakView都会内存泄露
- LeakView传入的是application,若是不添加到viewgroup,则activity不会内存泄露,LeakView自己肯定会内存泄露
3.LeakView传入的是application,若是添加到viewgroup,activity和LeakView都会内存泄露
4.LeakView传入的application,若是添加到viewgroup,但是在ondestroy时执行移除,则activity不会内存泄露,LeakView自己肯定会内存泄露。
结论:
一般来说,view是和activity绑定生命周期会比较好,activity销毁,view也跟着销毁,所以view的构造传入activity是比较合理的。
如果view中有耗时操作,则应该在activity调用ondestroy时,中断该操作。