Android-内存优化

释放双眼,带上耳机,听听看~!

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

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

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

 

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

Android-内存优化

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

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

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

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

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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1 private static Drawable sBackground;  
2
3  @Override  
4  protected void onCreate(Bundle state) {  
5    super.onCreate(state);  
6
7    TextView label = new TextView(this);  
8    label.setText("Leaks are bad");  
9
10    if (sBackground == null) {  
11      sBackground = getDrawable(R.drawable.large_bitmap);  
12    }  
13    label.setBackgroundDrawable(sBackground);  
14
15    setContentView(label);  
16  }  
17

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1public class SingleInstanceDemo {
2
3    private static SingleInstanceDemo mInstance;
4    private Context mContext;
5
6    private SingleInstanceDemo(Context context){
7        this.mContext = context;
8    }
9
10    public static SingleInstanceDemo newInstance(Context context){
11        if(mInstance == null){
12            mInstance = new SingleInstanceDemo(context);
13        }
14        return sInstance;
15    }
16}
17

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


1
2
3
4
1    private SingleInstanceDemo(Context context){
2        this.mContext = context.getApplicationContext();
3    }
4

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

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

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

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


1
2
3
4
5
1 if(!bitmap.isRecycled()){
2        bitmap.recycle();
3        bitmap = null;
4  }  
5

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


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

对图片质量进行压缩 


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

对图片尺寸进行压缩 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
1  /**
2     * 按图片尺寸压缩 参数是bitmap
3     * @param bitmap
4     * @param pixelW
5     * @param pixelH
6     * @return
7     */
8    public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
9        ByteArrayOutputStream os = new ByteArrayOutputStream();
10        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
11        if( os.toByteArray().length / 1024&gt;512) {//判断如果图片大于0.5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
12            os.reset();
13            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中
14        }
15        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
16        BitmapFactory.Options options = new BitmapFactory.Options();
17        options.inJustDecodeBounds = true;
18        options.inPreferredConfig = Bitmap.Config.RGB_565;
19        BitmapFactory.decodeStream(is, null, options);
20        options.inJustDecodeBounds = false;
21        options.inSampleSize = computeSampleSize(options , pixelH &gt; pixelW ? pixelW : pixelH ,pixelW * pixelH );
22        is = new ByteArrayInputStream(os.toByteArray());
23        Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
24        return newBitmap;
25    }
26
27
28    /**
29     * 动态计算出图片的inSampleSize
30     * @param options
31     * @param minSideLength
32     * @param maxNumOfPixels
33     * @return
34     */
35    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
36        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
37        int roundedSize;
38        if (initialSize &lt;= 8) {
39            roundedSize = 1;
40            while (roundedSize &lt; initialSize) {
41                roundedSize &lt;&lt;= 1;
42            }
43        } else {
44            roundedSize = (initialSize + 7) / 8 * 8;
45        }
46        return roundedSize;
47    }
48
49    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
50        double w = options.outWidth;
51        double h = options.outHeight;
52        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
53        int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
54        if (upperBound &lt; lowerBound) {
55            return lowerBound;
56        }
57        if ((maxNumOfPixels == -1) &amp;&amp; (minSideLength == -1)) {
58            return 1;
59        } else if (minSideLength == -1) {
60            return lowerBound;
61        } else {
62            return upperBound;
63        }
64    }
65

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


1
2
1       mBitmapRefs.add(new SoftReference(bitmap));
2

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

给TA打赏
共{{data.count}}人
人已打赏
安全经验

如何避免Adsense违规封号

2021-10-11 16:36:11

安全经验

安全咨询服务

2022-1-12 14:11:49

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索