1. 不合理的单例模式、静态Activity、Context等
一. LeakCanary介绍
1. 介绍
LeakCanary是一个检测内存泄露的开源类库,以可视化的方式 轻松检测内存泄露,并且在出现内存泄漏时及时通知开发者,省去手工分析hprof的过程。
Github:LeakCanary
2. 用法
Step1:在app的build.gradle的dependencies节点中添加以下两行
1.debugCompile'com.squareup.leakcanary:leakcanary-android:1.3'
2.releaseCompile'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
Step2:在Application中的onCreate方法中注册
1.publicclassLeakApplicationextendsApplication{
2.
3.@Override
4.publicvoidonCreate(){
5.super.onCreate();
6.LeakCanary.install(this);
7.}
8.
9.}
Step3:启动应用,等到memory leak发生,当内存泄漏发生时,launcher上生成一个图标(名称为Leaks),点击进去,即可看到完整的内存泄漏的引用路径。实际测试发现,点击Leaks后,页面上可能出现一段时间的空白,等待一段时间才出现内存泄漏的引用路径。
内存泄漏后桌面上产生的图标
二. 常见泄漏方式
1. 不合理的单例模式、静态Activity、Context等
示例代码:
1.publicclassSingleInstance{
2.privatestaticSingleInstance sInstance;
3.privateContext mContext;
4.privateSingleInstance(Context context){
5.mContext = context;
6.}
7.publicstaticSingleInstancegetInstance(Context context){
8.if(sInstance ==null){
9.sInstance =newSingleInstance(context);
10.}
11.returnsInstance;
12.}
13.}
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.@Override
4.protectedvoidonCreate(Bundle savedInstanceState){
5.super.onCreate(savedInstanceState);
6.setContentView(R.layout.activity_second);
7.SingleInstance.getInstance(this);
8.}
9.
10.}
LeakCanary提示:
不合理的SingleInstance导致的内存泄漏
原因分析:
这种场景非常常见!当SecondActivity销毁时,SingleInstance仍持有SecondActivity的对象的引用(mContext),导致SecondActivity对象不能释放。
解决方法:
可使用Application的Context代替Activity。
2. 持有Activity内的静态View
示例代码:
1.publicclassTestDataModel{
2.privatestaticTestDataModel sInstance;
3.privateTextView mRetainedTextView;
4.publicstaticTestDataModelgetInstance(){
5.if(sInstance ==null) {
6.sInstance =newTestDataModel();
7.}
8.returnsInstance;
9.}
10.publicvoidsetRetainedTextView(TextView textView){
11.mRetainedTextView = textView;
12.}
13.}
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.@Override
4.protectedvoidonCreate(Bundle savedInstanceState){
5.super.onCreate(savedInstanceState);
6.setContentView(R.layout.activity_second);
7.TextView textView = (TextView) findViewById(R.id.test_text_view);
8.TestDataModel.getInstance().setRetainedTextView(textView);
9.}
10.
11.}
LeakCanary提示:
持有静态View导致的内存泄漏
原因分析:
TestDataModel 持有Activity中的TextView的静态引用,而TextView又持有SecondActivity的引用,从而导致SecondActity在onDestory之后不能释放
解决方法:
尽量避免这种用法
3.较长生命周期的匿名内部类
示例代码:
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.@Override
4.protectedvoidonCreate(Bundle savedInstanceState){
5.super.onCreate(savedInstanceState);
6.setContentView(R.layout.activity_second);
7.testLeakMemory();
8.}
9.
10.privatevoidtestLeakMemory(){
11.newThread(newRunnable() {
12.@Override
13.publicvoidrun(){
14.SystemClock.sleep(200000);
15.}
16.}).start();
17.}
18.
19.}
LeakCanary提示:
原因分析:
SecondActivity中的匿名内部类持有SecondActivity的引用,并且该匿名内部类的生命周期比Activity要长
解决方法:
SecondActivity在销毁时,应取消内部类对其的引用
4.Handler中有生命周期较长的匿名内部类
示例代码:
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.privateHandler mHandler;
4.
5.@Override
6.protectedvoidonCreate(Bundle savedInstanceState){
7.super.onCreate(savedInstanceState);
8.setContentView(R.layout.activity_second);
9.testLeakMemory();
10.}
11.
12.privatevoidtestLeakMemory(){
13.HandlerThread thread =newHandlerThread("test");
14.thread.start();
15.mHandler =newHandler(thread.getLooper());
16.mHandler.post(newRunnable() {
17.@Override
18.publicvoidrun(){
19.SystemClock.sleep(200000);
20.}
21.});
22.}
23.}
LeakCanary提示:
Handler中有匿名内部类Runnable
原因分析:
mHandler在post时,其参数为匿名内部类,持有SecondActivity的引用,并且该Runnable的生命周期比SecondActivity长
解决方法:
SecondActivity在onDestory时,应移除mHandler中未完成的任务
5. 资源未关闭造成的内存泄漏
Cursor、BroadcastReceiver、 TypedArray、File 、Stream 、Bitmap等使用完毕后应及时释放或关闭或反注册。
示例代码(Cursor):
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.@Override
4.protectedvoidonCreate(Bundle savedInstanceState){
5.super.onCreate(savedInstanceState);
6.setContentView(R.layout.activity_second);
7.testLeakMemory();
8.}
9.
10.privatevoidtestLeakMemory(){
11.Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,null);
12.if(cursor !=null){
13.while(cursor.moveToNext()){
14.Log.d("test",cursor.getString(0));
15.}
16.}
17.}
18.
19.}
示例代码(BroadcastReceiver):
1.publicclassSecondActivityextendsAppCompatActivity{
2.
3.@Override
4.protectedvoidonCreate(Bundle savedInstanceState){
5.super.onCreate(savedInstanceState);
6.setContentView(R.layout.activity_second);
7.testLeakMemory();
8.}
9.
10.privatevoidtestLeakMemory(){
11.IntentFilter filter =newIntentFilter(Intent.ACTION_MEDIA_BUTTON);
12.TestReceiver receiver =newTestReceiver();
13.registerReceiver(receiver,filter);
14.}
15.
16.privateclassTestReceiverextendsBroadcastReceiver{
17.@Override
18.publicvoidonReceive(Context context, Intent intent){
19.
20.}
21.}
22.}
解决方法:
Cursor、BroadcastReceiver、 TypedArray、File 、Stream 、Bitmap等使用完毕后应及时关闭或释放