Android 内存溢出、内存泄漏

Bitmap对象的处理

不要在主线程中处理图片

使用Bitmap对象要用recycle释放

// Bitmap对象没有被回收
 if (!bitmapObject.isRecyled()) {
     // 释放  
     bitmapObject.recycle(); 
     // 提醒系统及时回收 
     System.gc(); 
     }  

控制图片的大小,压缩大图,高效处理,加载合适属性的图片。
当我们有些场景是可以显示缩略图的时候,就不要调用网络请求加载大图,例如在RecyclerView中,我们在上下滑动的时候,就不要去调用网络请求,当监听到滑动结束的时候,才去加载大图,以免上下滑动的时候产生卡顿现象。

大量的图片、音频、视频处理

建议使用第三方,或者JNI来进行处理

资源未及时关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,Cursor,File,Stream,ContentProvider,Bitmap,动画,I/O,数据库,网络的连接等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

        广播BraodcastReceiver:记得注销注册unregisterReceiver();

        文件流File:记得关闭流InputStream / OutputStream.close();

        数据库游标Cursor:使用后关闭游标cursor.close();

        对于图片资源Bitmap:当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存,再赋为null

        动画:属性动画或循环动画,在Activity退出时需要停止动画。在属性动画中有一类无限循环动画,如果在Activity中播放这类动画并且在onDestroy中没有去停止动画,那么这个动画将会一直播放下去,这时候Activity会被View所持有,从而导致Activity无法被释放。在Activity中onDestroy去调用objectAnimator.cancel()来停止动画。

 public class LeakActivity extends AppCompatActivity {
     private TextView textView;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_leak);
         textView = (TextView) findViewById(R.id.text_view);
         ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView, "rotation", 0, 360);
         objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
         objectAnimator.start();
     }
 }

        集合对象及时清理,使得JVM回收:我们通常会把对象存入集合中,当不使用时,清空集合,让相关对象不再被引用;

 objectList.clear();
 objectList=null;

不要使用String进行字符串拼接

严格的讲,String拼接只能归结到内存抖动中,因为产生的String副本能够被GC,不会造成内存泄露。

频繁的字符串拼接,使用StringBuffer或者StringBuilder代替String,可以在一定程度上避免OOM和内存抖动。

第三方库

对于EventBus,RxJava等一些第三方开源框架的使用,若是在Activity销毁之前没有进行解除订阅将会导致内存泄漏

如果使用Context ,尽可能使用Applicaiton的Context

单例模式造成的内存泄漏,如context的使用,单例中传入的是activity的context,在关闭activity时,activity的内存无法被回收,因为单例持有activity的引用。

在context的使用上,应该传入application的context到单例模式中,这样就保证了单例的生命周期跟application的生命周期一样。

因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。

单例模式应该尽量少持有生命周期不同的外部对象,一旦持有该对象的时候,必须在该对象的生命周期结束前null

 public class TestManager {
     private static TestManager instance;
     private Context context;
 
     private TestManager(Context context) {
         this.context = context;
     }
 
     public static TestManager getInstance(Context context) {
         if (instance != null) {
             instance = new TestManager(context);
         }
         return instance;
     }
 }

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:
        1、传入的是Application的Context:这将没有任何问题,因为单例的生命周期和Application的一样长 ;
        2、传入的是Activity的Context:当这个Context所对应的Activity退出时,由于该Context和Activity的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。
所以正确的单例应该修改为下面这种方式:

    public class TestManager {
        private static TestManager instance;
        private Context context;
    
        private TestManager(Context context) {
            this.context = context.getApplicationContext();
        }
    
        public static TestManager getInstance(Context context) {
            if (instance != null) {
                instance = new TestManager(context);
            }
            return instance;
        }
    }

这样不管传入什么Context最终将使用Application的Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏。

注销监听器

在onPause()/onDestroy()方法中解除监听器,包括在Android自己的Listener,Location Service或Display Manager Service以及自己写的Listener。

你可能感兴趣的:(内存泄漏)