2019独角兽企业重金招聘Python工程师标准>>>
因为静态变量造成的内存泄漏
public class MyApplication {
private static MyApplication application;
private MyApplication(Context context){
}
public static MyApplication getApplication(Context context){
if(application==null){
application = new MyApplication(context);
}
return application;
}
}
我创建了一个MyApplication 类,在构造方法中,我传入一个Context。Context 包括Application\Activity\Service,如果我传入的是Application,那么不会有什么影响,因为我所需要的时间是一整个程序的生命周期。
但是我如果传入的是Activity,那么,当我退出该Activity时,因为被static 修饰,单例对象持有对该Activity的引用,导致该Activity不会被回收,从而产生内存泄漏。
解决办法:
在获取单例的方法中,把原有的context 改成application = new MyApplication(context.getApplicationContext());
把传入的Context的生命周期设置成与Application 一样长,这样在使用单例的时候就和导入的Context 类型无关,防止了内存泄漏。
Handler 的错误使用
public class Act_test extends Activity{
private static final int WHAT = 1;
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case WHAT:
//handle it
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Message message = Message.obtain();
message.what = WHAT;
mHandler.sendMessage(message);
}
}
当我们创建出一个Handler 的时候,代码中的mHandler为Handler 的非静态内部类的实例,所以mHandler 持有对外部类,即Activity 的引用。并且Handler 中的Looper不断轮询消息队列中的message,message 又持有mHandler的引用,但mHandler又持有Activity 的引用,因此activity无法被回收。
解决方式:
public class Act_test extends Activity{
private static final int WHAT = 1;
private MyHandler mHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Message message = Message.obtain();
message.what = WHAT;
mHandler.sendMessage(message);
}
static class MyHandler extends Handler{
WeakReference<Context> reference;
public MyHandler(Context context) {
// TODO Auto-generated constructor stub
reference = new WeakReference<Context>(context);
}
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(reference!=null){
if(msg.what==WHAT){
//handle it
}
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
首先,创建一个Handler 的静态内部类,这样,它将不再持有外部类的引用,并且将持有它的Context 进行弱引用,确保Activity 可以及时被回收。在前面分析过,Handler 内存泄漏是因为消息队列中还有未处理的Message ,所以当该Activity 被销毁时,将消息队列中的Message 清空即可。
非静态内部类的静态实例的错误使用
public class Act_test extends Activity{
private static Test mTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(mTest==null){
mTest = new Test();
}
}
class Test{
}
}
这种情况多使用于反复使用同一个Activity 为了避免重复创建资源。
但是这种写法同样存在着隐患,因为在该Activity中,我创建了一个Test 类的静态实例,每次启动Activity 都会使用该单例。但是由于非静态内部类持有外部类的使用,并且该非静态内部类又创建了一个静态的实例,这导致了该单例的存活时间与application的生命周期一样长,于是会一直持有该Activity的引用,导致其无法被回收。
解决方案:static class Test{
}
将Test 设置为静态内部类。
不正确使用线程,造成内存泄漏
public class ThreadActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyThread().start();
}
private class MyThread extends Thread {
@Override
public void run() {
super.run();
dosomthing();
}
}
private void dosomthing(){
}
}
假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,
一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。
由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,
MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。
这种线程导致的内存泄露问题应该如何解决呢?
- 将线程的内部类,改为静态内部类。
- 在线程内部采用弱引用保存Context引用。
-
public class ThreadAvoidActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyThread(this).start(); } private void dosomthing() { } private static class MyThread extends Thread { WeakReference<ThreadAvoidActivity> mThreadActivityRef; public MyThread(ThreadAvoidActivity activity) { mThreadActivityRef = new WeakReference<ThreadAvoidActivity>( activity); } @Override public void run() { super.run(); if (mThreadActivityRef == null) return; if (mThreadActivityRef.get() != null) mThreadActivityRef.get().dosomthing(); // dosomthing } } }
上面的两个步骤其实是切换两个对象的双向强引用链接
静态内部类:切断Activity 对于 MyThread的强引用。
弱引用: 切断MyThread对于Activity 的强引用
AsynTask 内部类会如何呢?
有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,
Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,
该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,
因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。
资源没有及时关闭
在开发中,例如Cursor、File、IOStream等资源在使用后要及时进行关闭,避免造成内存的浪费。
TypedArray、Bitmap等要及时进行recycle。
EventBus 、BroadcastReceiver注册后避免重复注册,使用后及时销毁。