Android常见内存泄漏和LeakCanary的使用

App有内存泄漏就是埋了一颗定时炸弹, 累积多了终将导致OOM. 下面列举一些常见导致内存泄漏的代码.

未注销导致的内存泄漏

使用了广播, EventBus等等, 会导致Activity无法被GC回收

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        EventBus.getDefault().register(this);
    }

    @Subscribe
    public void onEvent(MessageEvent msg) {

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //未移除注册的EventBus
//        EventBus.getDefault().unregister(this);
    }

非静态内部类使用不当导致的内存泄漏

非静态内部类会持有外部类引用, 如果Activity中包含了静态的内部类成员就会导致内存泄漏

private static InnerClass mInnerClass;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        mInnerClass = new InnerClass();
    }

    //非静态内部类持有外部类引用
    public class InnerClass {
        private String msg;

        public InnerClass() {
        }

    }

匿名类使用不当导致的内存泄漏

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        //匿名类持有外部类引用
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        }.execute();
    }

工具类使用不当导致的内存泄漏

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        String s = Util.getInstance(this).doSomething();
    }
public class Util {
    private volatile static Util instance;
    private static Context mContext;

    private Util(Context context) {
        mContext = context;
    }
//传进来的context被静态持有
    public static Util getInstance(Context context) {
        if (instance == null) {
            synchronized (Util.class) {
                if (instance == null) {
                    instance = new Util(context);
                }
            }
        }
        return instance;
    }

    public static String doSomething() {
        String string = mContext.getResources().getString(R.string.app_name);
        return string;
    }
}

写Util的时需要Context的时候, 判断一下, 如果可以用ApplicationContext优先使用.
可以用ApplicationContext的情况下在Application里面加上一个getInstance()方法, 就不用传Context进来了.

public class App extends Application {
    private static App instance;

    public static App getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }
}

线程使用不当导致的内存泄漏

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        //Activity实例被Runnable持有, Runnable传入l一个异步线程,
        // 此线程和此Acitivity生命周期不一致的时候,就造成了Activity的泄露。
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) ;
            }
        }).start();
    }

Handle使用不当导致的内存泄漏

 private Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        //Handler 为非静态内部类,它会持有外部类的引用
        mLeakyHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //doSomething
            }
        }, 1000 * 20);
    }

静态集合使用不当导致的内存泄漏

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //静态集合没有移除元素
//        ActivityCollector.removeActivity(this);
    }
public class ActivityCollector {

    public static List<Activity> activities = new ArrayList<Activity>();

    public static void addActivity(Activity activity) {
        activities.add(activity);
    }

    public static void removeActivity(Activity activity) {
        activities.remove(activity);
    }
}

使用弱引用避免内存泄漏

在 Activity 中避免使用非静态内部类,比如上面我们将 Handler 声明为静态的,则其存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去

public class WeakReferenceActivity extends AppCompatActivity {

    //将 Handler 声明为静态 没有了Activity的引用, 无法直接引用其变量或方法,
    // 使用弱引用WeakReference来解决这个问题
    private static class DBHandler extends Handler {

        //弱引用, 而不是使用外部类this或者传进来
        private final WeakReference<WeakReferenceActivity> mActivity;

        public DBHandler(WeakReferenceActivity activity) {
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            WeakReferenceActivity activity = mActivity.get();
            if (activity != null) {
                Toast.makeText(activity, "what: " + msg.what, Toast.LENGTH_SHORT).show();
            }
        }

    }

    private final DBHandler mHandler = new DBHandler(this);

    private static final Runnable sRunnable = new Runnable() {
        @Override
        public void run() {
            Toast.makeText(App.getInstance(), "sRunnable run()", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_weak_reference);
        mHandler.postDelayed(sRunnable, 1000 * 20);
    }

    public void onClick(View view) {
        mHandler.sendEmptyMessage(new Random().nextInt(10));
    }
}

LeakCanary的使用

  • build.gradle dependencies{…}添加
    //https://github.com/square/leakcanary
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
  • Application配置
public class App extends Application {
    private static App instance;
    private static RefWatcher sRefWatcher;

    public static RefWatcher getRefWatcher() {
        return sRefWatcher;
    }

    public static App getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }
        sRefWatcher = LeakCanary.install(this);
        // Normal app init code...
        instance = this;
    }
}
  • AndroidManifest.xml
<application
    android:name="yourApp"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
</application>

检测Fragment内存泄漏

public class LeakFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_common, container, false);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) ;
            }
        }).start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //onDestroy中添加
        App.getRefWatcher().watch(this);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值