Android中肯定会遇到的问题——内存泄漏
今天来谈一谈Android做久了肯定会遇到的内存泄漏问题,我相信很多人都碰到了这个问题,所以今天本着学习交流的态度写下了这篇文章,希望大家多多交流。
什么是内存泄漏?
众所周知,Java是有垃圾回收机制的,所以我们在开发中不用像搞C++那样要记得释放内存等等问题,但是就是因为这么省心所以才会有内存泄漏的问题。
在Android开发中本来一个对象不需要使用了,本来是要回收的,但是由于另一个正在使用的对象持有它的引用导致它不能正常的被回收,这样就让本来该回收的对象还留在内存中,内存泄漏就产生啦!
内存泄漏的响应
那么有人要问了,漏了之后有什么影响呢?其实呢它可能会造成你的应用OOM,有人又要问了OOM了就OOM呗 管我啥事? 这。。。如果真要这么问,那我无言以对。 不过我相信大家都是高水平的同志,这些问题都不会问的。因为我们会为了用户体验更好,性能更佳去努力,这才是我们做开发的目的!
分析常见泄漏
一、Handler造成的内存泄漏
先来看一个Hanlder的代码
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//do something
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
Message message = Message.obtain();
handler.sendMessage(message);
}
}
这个估计大家都写过这样的Handler,在这个例子中handler 不是静态的匿名内部类,它持有外部的Activity的引用,由于Looper会在线程中不断轮询消息,如果Activity退出时很有可能还有未处理完成的消息,但是Message持有了handler的引用,handler又持有activity的引用,所以导致activity不能正常的被回收掉。所以,引发了内存泄漏。
我们来看一下Handler的源码
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
源码中有一条警告写的是:The following Handler class should be static or leaks might occur 意思为Handler要使用static来修饰否则会引发内存泄漏。 所以我们遵循Goodle的指示来修改我们的代码。
public class MainActivity extends AppCompatActivity {
private MyHandler handler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
//do something
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData() {
Message message = Message.obtain();
handler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
}
创建一个静态Handler内部类,这样在回收时也可以回收Handler持有的对象,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,所以我们使用了handler.removeCallbacksAndMessages(null)来处理。
二、单例模式引发的泄漏
单例,相比大家都写过,不过使用不当也会发生泄漏,话不多说来看代码吧。
public class Test{
private static Test instance;
private Context context;
private Test(Context context) {
this.context = context;
}
public static getInstance(Context context) {
if (instance == null) {
instance = new Test(context);
}
return instance;
}
}
这个是普通的一个单例模式,当它执行是需要传入Context 所以Context的生命周期就是非常重要的一个因素。 我们平时可能都会传入一个Activity的引用,因为Activity继承Context,所以当Activity退出时Context还继续持有Activity对象,这样就导致activity不能被回收,所以引发内存泄漏。
正确做法是传入一个application的上下文,来看代码:
public class Test{
private static Test instance;
private Context context;
private Test(Context context) {
this.context = context.getApplicationContext();
}
public static getInstance(Context context) {
if (instance == null) {
instance = new Test(context);
}
return instance;
}
}
这样不管传入什么,Context最终将使用Application的Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏。
三、资源引发内存泄漏问题。
对于使用了BraodcastReceiver,ContentObserver,File,Cursor等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
好了,今天就到这里吧,有想法的同志记得来下面留言啊,让我们一起进步!!
我想推广下我的个人博客