android app性能优化-有哪些方法可以减少内存使用:

时间:2021-07-09 20:52:55
Bitmap:

Bitmap是内存消耗大户,绝大多数的OOM崩溃都是在操作Bitmap时产生的,下面来看看如何几个处理图片的方法:


图片显示:

我们需要根据需求去加载图片的大小。

例如在列表中仅用于预览时加载缩略图(thumbnails )。

只有当用户点击具体条目想看详细信息的时候,这时另启动一个fragment/activity/对话框等等,去显示整个图片


图片大小:

直接使用ImageView显示bitmap会占用较多资源,特别是图片较大的时候,可能导致崩溃。 
使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。 
属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。 

 

  1. BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();  
  2. bitmapFactoryOptions.inJustDecodeBounds = true;  
  3. bitmapFactoryOptions.inSampleSize = 2;  
  4. // 这里一定要将其设置回false,因为之前我们将其设置成了true    
  5. // 设置inJustDecodeBounds为true后,decodeFile并不分配空间,即,BitmapFactory解码出来的Bitmap为Null,但可计算出原始图片的长度和宽度    
  6. options.inJustDecodeBounds = false;  
  7. Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);  

 

图片像素:

Android中图片有四种属性,分别是:
ALPHA_8:每个像素占用1byte内存 
ARGB_4444:每个像素占用2byte内存 
ARGB_8888:每个像素占用4byte内存 (默认)
RGB_565:每个像素占用2byte内存 
 
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。 所以在对图片效果不是特别高的情况下使用RGB_565(565没有透明度属性)

图片回收:

使用Bitmap过后,就需要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间,而不要等Android系统来进行释放。

下面是释放Bitmap的示例代码片段。

 

  1. // 先判断是否已经回收  
  2. if(bitmap != null && !bitmap.isRecycled()){  
  3.     // 回收并且置为null  
  4.     bitmap.recycle();  
  5.     bitmap = null;  
  6. }  
  7. System.gc();  


捕获异常:

经过上面这些优化后还会存在报OOM的风险,所以下面需要一道最后的关卡——捕获OOM异常:

 

  1. Bitmap bitmap = null;  
  2. try {  
  3.     // 实例化Bitmap  
  4.     bitmap = BitmapFactory.decodeFile(path);  
  5. catch (OutOfMemoryError e) {  
  6.     // 捕获OutOfMemoryError,避免直接崩溃  
  7. }  
  8. if (bitmap == null) {  
  9.     // 如果实例化失败 返回默认的Bitmap对象  
  10.     return defaultBitmapMap;  
  11. }  

其他小tips:

 

对常量使用static final修饰符

让我们来看看这两段在类前面的声明:

static int intVal = 42;
static String strVal = "Hello, world!";
编译器会生成一个叫做clinit的初始化类的方法,当类第一次被使用的时候这个方法会被执行。方法会将42赋给intVal,然后把一个指向类中常量表 的引用赋给strVal。当以后要用到这些值的时候,会在成员变量表中查找到他们。 下面我们做些改进,使用“final”关键字:

static final int intVal = 42;
static final String strVal = "Hello, world!";

现在,类不再需要clinit方法,因为在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个字符串常量,而不是使用成员变量。

将一个方法或类声明为final不会带来性能的提升,但是会帮助编译器优化代码。举例说,如果编译器知道一个getter方法不会被重载,那么编译器会对其采用内联调用。

你也可以将本地变量声明为final,同样,这也不会带来性能的提升。使用“final”只能使本地变量看起来更清晰些(但是也有些时候这是必须的,比如在使用匿名内部类的时候)。

 

静态方法代替虚拟方法

如果不需要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的做法,因为你可以从方法声明中看出调用该方法不需要更新此对象的状态。

减少不必要的全局变量

尽量避免static成员变量引用资源耗费过多的实例,比如Context

 

因为Context的引用超过它本身的生命周期,会导致Context泄漏。所以尽量使用Application这种Context类型。 你可以通过调用Context.getApplicationContext()或 Activity.getApplication()轻松得到Application对象。 
 

避免创建不必要的对象

最常见的例子就是当你要频繁操作一个字符串时,使用StringBuffer代替String。

对于所有所有基本类型的组合:int数组比Integer数组好,这也概括了一个基本事实,两个平行的int数组比 (int,int)对象数组性能要好很多。

总体来说,就是避免创建短命的临时对象。减少对象的创建就能减少垃圾收集,进而减少对用户体验的影响。

 
避免内部Getters/Setters
在Android中,虚方法调用的代价比直接字段访问高昂许多。通常根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段经常被访问的类中宜采用直接访问。


避免使用浮点数

通常的经验是,在Android设备中,浮点数会比整型慢两倍。

 

使用实体类比接口好

 

假设你有一个HashMap对象,你可以将它声明为HashMap或者Map:

Map map1 = new HashMap();
HashMap map2 = new HashMap();

哪个更好呢?

按照传统的观点Map会更好些,因为这样你可以改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,但是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。如果HashMap完全适合你的程序,那么使用Map就没有什么价值。如果有些地方你不能确定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(当然公共API是一个例外:一个好的API常常会牺牲一些性能)

避免使用枚举

枚举变量非常方便,但不幸的是它会牺牲执行的速度和并大幅增加文件体积。

使用枚举变量可以让你的API更出色,并能提供编译时的检查。所以在通常的时候你毫无疑问应该为公共API选择枚举变量。但是当性能方面有所限制的时候,你就应该避免这种做法了。

 

for循环

访问成员变量比访问本地变量慢得多,如下面一段代码:

 

  1. for(int i =0; i < this.mCount; i++)  {}  

永远不要在for的第二个条件中调用任何方法,如下面一段代码:

 

  1. for(int i =0; i < this.getCount(); i++) {}  

对上面两个例子最好改为:

 

  1. int count = this.mCount; / int count = this.getCount();  
  2. for(int i =0; i < count; i++)  {}  
在java1.5中引入的for-each语法。编译器会将对数组的引用和数组的长度保存到本地变量中,这对访问数组元素非常好。 但是编译器还会在每次循环中产生一个额外的对本地变量的存储操作(如下面例子中的变量a),这样会比普通循环多出4个字节,速度要稍微慢一些:

 

  1. for (Foo a : mArray) {  
  2.     sum += a.mSplat;  
  3. }