android 常见的内存泄漏

好久没写博客了,今天提辞职了,希望以后还能一如既往的继续写博客,今天写下常见的内存泄漏的案例,可能在我们平时开发中经常这么写,但是并没发现有什么内存泄漏问题? 分为如下几种

第一种情况静态变量引起的内存泄露

我们在获取屏幕的宽和高时,都会写一个通用的工具类,方便使用:


package com.leak;
import android.content.Context;
import android.view.WindowManager;
/**
 * Created by zhouguizhi on 2017/10/23.
 */
public class CommonUtils {
    private static CommonUtils INSTANCE = null;
    private Context mContext;
    private WindowManager windowManager;
    public CommonUtils(Context context) {
        this.mContext = context;
        windowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    public static CommonUtils getInstance(Context context){
       if(null==INSTANCE){
           INSTANCE = new CommonUtils(context);
       }
       return INSTANCE;
    }
    public int getScreenWidth(){
        if(windowManager==null){
            return -1;
        }
        return windowManager.getDefaultDisplay().getWidth();
    }
    public int getScreenHeight (){
        if(windowManager==null){
            return -1;
        }
        return windowManager.getDefaultDisplay().getHeight();
    }
}


在MainActivity调用:


package com.leak;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CommonUtils.getInstance(this).getScreenHeight();
    }
}


然后通过Link检查会发现这段代码有啥问题:




解决方法:传Application就行

第二种情况非静态内部类引起内存泄露


package com.leak;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private void loadData() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        int a  =b;
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}


这个通过Link既然没检查出来,我就使用了LeakCanary,运行起来发现确实有内存泄漏的问题:




其实问题是出现在这段话:


int a  =b;


因为new Thread()是一个匿名内部类,而a=b,其实是a=MainActivity.this.b,隐士的持有MainActivity实例,这样比如在你调用网络请求还没结束的时候,这个时候你退出这个界面的,但是这个线程还会在运行,这样就导致这个Activity无法销毁,

解决办法:

把loadData()方法改为静态方法即可!因为静态内部类不会隐士持有外部类实例.

这样虽然解决了内存泄漏的问题,但是无法使用外部类的变量了,而你又想使用外部类的变量怎么办?

我改成这样:


package com.leak;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private   void loadData() {
        MyThread myThread = new MyThread(this);
        myThread.start();
    }
    private class MyThread extends Thread{
        private MainActivity activity;
        public MyThread(MainActivity activity){
            this.activity = activity;
        }
        @Override
        public void run() {
            super.run();
            while (true){
                    try {
                       int a = activity.b;
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}


其实问题还是一样的:




一般是这么解决的:


package com.leak;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.lang.ref.WeakReference;

@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private   void loadData() {
        MyThread myThread = new MyThread(this);
        myThread.start();
    }
    private class MyThread extends Thread{
        private WeakReference<MainActivity> weakReference;
        public MyThread(MainActivity activity){
            this.weakReference = new WeakReference<>(activity);
        }
        @Override
        public void run() {
            super.run();
            while (true){
                    try {
                        MainActivity activity = weakReference.get();
                       int a = activity.b;
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        }
    }
}


使用软引用或者弱引用来解决的.

第三种情况Handler引起的内存泄漏


package com.leak;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private   void loadData() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                int a = b;
            }
        },2000);
    }
}


平时也有人这么写,之前我干过这种事,

这个和上面第二种情况是一样的,




发现内存泄漏了,那我改成这样:


package com.leak;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what==100){
                int a = b;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Message msg = new Message();
        msg.what=100;
        mHandler.sendMessageDelayed(msg,20000);
    }
}


其实还是会出现内存泄漏:因为mHandler是匿名内部类的实例,会引用外部对象MainActivity.this。如果Handler在MaiinActivity退出的时候,它可能还活着,这时候就会一直持有MainActivity

解决方法:


package com.leak;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.lang.ref.WeakReference;
@SuppressWarnings("unused")
public class MainActivity extends AppCompatActivity {
    private final int b = 10;
    private static class MyHandler extends Handler{
        private WeakReference<MainActivity> weakReference;//设置软引用保存,当内存一发生GC的时候就会回收。
        public MyHandler(MainActivity mainActivity) {
            this.weakReference = new WeakReference<>(mainActivity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity main =  weakReference.get();
            if(main==null||main.isFinishing()){
                return;
            }
            switch (msg.what){
                case 100:
                    int a = main.b;
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Message msg = new Message();
        msg.what=100;
        MyHandler mHandler = new MyHandler(this);
        mHandler.sendMessageDelayed(msg,20000);
    }
}


第四种情况 资源未关闭引起的内存泄露

比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute,attr.recycle()回收。当不需要使用的时候,要记得及时释放资源。否则就会内存泄露

第五种情况无限循环动画引起的内存泄露

没有在onDestroy中停止动画,否则Activity就会变成泄露对象。比如:轮播图效果

猜你喜欢

转载自blog.csdn.net/coderinchina/article/details/78323743
今日推荐