Android-内存优化

内存优化说了下UI,就说说内存吧。

1,内存泄漏memory leak:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。 
2,内存溢出 out of memory:指程序申请内存时,没有足够的内存供申请者使用,内存不够用,此时就会报错OOM,即所谓的内存溢出。

     移动开发和web开发的最大的区别是设备资源受限,对一般手机应用,这个资源是相当有限的,当应用程序处理大资源的资源,如图片或视频等媒体资源时 ,数量一多,时间一长,OOM是很容易出现的,不同手机不同机型也会有不同的效果,你开发使用的测试机没有问题,但是有一些手机就会出现。虽然JAVA有垃圾回收机制,但也存在内存泄露。为什么呢?

 

因为垃圾回收机制,没能回收,出现内存泄露,有内存泄漏,所以内存被占用越来越多,那么GC会更容易被触发,GC会越来越频发,但是当GC的时候所有的线程都是暂停状态的,需要处理的对象数量越多耗时越长,所以这也会造成卡顿。

如果有效避免这些,首先要养成良好的编程习惯,开启记得关闭,注册记得取消注册,集合不用记得清空,另外还有java的四大引用等等。

android中常见的原因主要有以下几个:

  • 数据库的cursor没有关闭。
  • 构造adapter没有使用缓存contentview。
  • 调用registerReceiver()后未调用unregisterReceiver().
  • 未关闭InputStream/OutputStream。
  • Bitmap使用后未调用recycle()。
  • Context泄漏。
  • static关键字等。

资源未关闭造成的内存泄漏,这个不必多说,记得关闭。

  • 网络、文件等流忘记关闭
  • Service 执行完后忘记 stopSelf()
  • EventBus 等观察者模式的框架忘记手动解除注册等

比较容易疏忽忘记的,是static静态,导致的Context引用无法回收。

static修饰符是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。我们看看google文档中的一个例子。

 private static Drawable sBackground;   

  @Override  
  protected void onCreate(Bundle state) {  
    super.onCreate(state);   

    TextView label = new TextView(this);  
    label.setText("Leaks are bad");   

    if (sBackground == null) {  
      sBackground = getDrawable(R.drawable.large_bitmap);  
    }  
    label.setBackgroundDrawable(sBackground);   

    setContentView(label);  
  }   

静态变量sBackground持有该Activity的Context,当Drawable与View连接之后,Drawable就将View 设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。最终该Context也没有得到释放,也发生了内存泄露。

再举个小例子,非常常用的单例模式,写一个SingleInstanceDemo的单例引用,这里将对象静态化:

public class SingleInstanceDemo {

    private static SingleInstanceDemo mInstance;
    private Context mContext;

    private SingleInstanceDemo(Context context){
        this.mContext = context;
    }

    public static SingleInstanceDemo newInstance(Context context){
        if(mInstance == null){
            mInstance = new SingleInstanceDemo(context);
        }
        return sInstance;
    }
}

当我们在Activity里面使用这个的时候,把我们Acitivty的context传进去,那么,这个单例就持有这个Activity的引用,当这个Activity没有用了,需要销毁的时候,因为这个单例还持有Activity的引用,所以无法GC回收,所以就出现了内存泄漏。解决方案也简单,改变构造方法:

    private SingleInstanceDemo(Context context){
        this.mContext = context.getApplicationContext();
    }

总结一下,静态变量的使用和Context引用的情况:
   第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
   第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。
   第三、使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;

最后说一下,我们的大胖子,bitmap内存泄露

Bitmap占用的内存实在是太多了,特别是分辨率大的图片,如果要显示多张那问题就更显著了。

第一:在用完Bitmap时,要 及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”,  还有就是, 虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试表明它并没能立即释放内存。故我们还需手动设置为NULL这样 还能大大的加速Bitmap的主要内存的释放。

 if(!bitmap.isRecycled()){
        bitmap.recycle();
        bitmap = null;
  }  

第二:压缩图片,加载一个缩小过的图片,可以设置一定的采样率,那么就可以大大减小占用的内存

BitmapFactory.Options options = new BitmapFactory.Options();  
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一  
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);  
preview.setImageBitmap(bitmap);  

对图片质量进行压缩 

           ByteArrayOutputStream baos = new ByteArrayOutputStream();  
            //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
            int options = 100;  
            //循环判断如果压缩后图片是否大于50kb,大于继续压缩  
            while ( baos.toByteArray().length / 1024>50) {  
                //清空baos  
                baos.reset();  
                bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);  
                options -= 10;//每次都减少10  
            }  
            //把压缩后的数据baos存放到ByteArrayInputStream中  
            ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
            //把ByteArrayInputStream数据生成图片  
            Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);  

对图片尺寸进行压缩 

  /**
     * 按图片尺寸压缩 参数是bitmap
     * @param bitmap
     * @param pixelW
     * @param pixelH
     * @return
     */
    public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
        if( os.toByteArray().length / 1024>512) {//判断如果图片大于0.5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            os.reset();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        BitmapFactory.decodeStream(is, null, options);
        options.inJustDecodeBounds = false;
        options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelW : pixelH ,pixelW * pixelH );
        is = new ByteArrayInputStream(os.toByteArray());
        Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
        return newBitmap;
    }


    /**
     * 动态计算出图片的inSampleSize
     * @param options
     * @param minSideLength
     * @param maxNumOfPixels
     * @return
     */
    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    }

    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
        if (upperBound < lowerBound) {
            return lowerBound;
        }
        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    }

第三:巧妙的运用软引用(SoftRefrence)

       mBitmapRefs.add(new SoftReference(bitmap)); 

第四:加载图片尽量使用缓存机制,缓存框架也很多,这里就不一一举列了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值