Android性能优化方案总结

Android的性能优化主要是从,布局优化,绘制优化,内存泄漏优化,响应速度优化,listview优化,bitmap优化,线程优化进行优化处理的。

1.布局优化和绘制优化:

减少视图层级可以有效的减少内存消耗,因为视图是一个树形结构,每次刷新和渲染都会遍历一次。 
布局优化的思想就是减少布局的层级,主要是采用include标签、merge标签、ViewStub标签,其中include主要用于布局重用,merge一般和include配合使用,它可以降低减少布局的层级。而ViewStub提供了按需加载的功能,当需要时才将ViewStub中的布局加入到内存中,提高了程序的初始化效率。、

布局的时候可以使用Java代码代替xml文件

一般情况下对于Android程序布局往往使用XML文件来编写,这样可以提高开发效率,但是考虑到代码的安全性以及执行效率,可以通过Java代码执行创建,虽然Android编译过的XML是二进制的,但是加载XML解析器的效率对于资源占用还是比较大的,Java处理效率比XML快得多,但是对于一个复杂界面的编写,可能需要一些套嵌考虑,如果你思维灵活的话,使用Java代码来布局你的Android应用程序是一个更好的方法。

过度绘制:

什么是过度绘制?

Overdraw(过度绘制)是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面, 如果不可见的UI也在做绘制的操作,这就会导致某些像素区域被绘制了多次,这就浪费大量的CPU以及GPU资源。

过度绘制产生的原因?

冗余的背景

嵌套的layout

解决方法:

移除Window默认的Background 
移除XML布局文件中非必需的Background 
按需求显示占位背景图片 
减少布局嵌套

2.重用系统资源

在进行布局的时候重用系统的资源

    1. 利用系统定义的id
    1. 利用系统的图片资源
    1. 利用系统的字符串资源
    1. 利用系统的Style
    1. 利用系统的颜色定义

3.响应速度优化

响应速度优化的核心思想是避免在主线程中做耗时操作,可以将这些耗时操作放在子线程中去执行,也就是采用异步处理的操作。

4.listview优化

1、convertView重用

2、ViewHolder优化:

使用ViewHolder的原因是findViewById方法耗时较大,如果控件个数过多,会严重影响性能,而使用ViewHolder主要是为了可以省去这个时间。通过setTag,getTag直接获取View

 
 
  • 1
  • 2

3、图片加载优化

如果ListView需要加载显示网络图片,我们尽量不要在ListView滑动的时候加载图片,那样会使ListView变得卡顿,所以我们需要在监听器里面监听ListView的状态,如果ListView滑动(SCROLL_STATE_TOUCH_SCROLL)或者被猛滑(SCROLL_STATE_FLING)的时候,停止加载图片,如果没有滑动(SCROLL_STATE_IDLE),则开始加载图片。

4.减少Item View的布局层级

这是所有layout都必须遵循的,布局层级过深会直接导致View的测量与绘制浪费大量的时间

5.adapter中的getView方法尽量少使用逻辑 
adapter中的getView方法尽量少做耗时操作 
adapter中的getView方法避免创建大量对象

不要在getView方法中做过于复杂的逻辑,可以想办法抽离到别的地方

6.将ListView的scrollingCache和animateCache设置为false

这两个属性,默认情况下是开启的,会消耗大量的内存,因此会频繁调用GC,我们可以手动将它关闭掉(视情况而定)

5.bitmap优化

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

图片显示:

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

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

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

图片大小(压缩图片):

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

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

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

图片像素:

Android中图片有四种属性,分别是: 
ALPHA_8:每个像素占用1byte内存 
ARGB_4444:每个像素占用2byte内存 
ARGB_8888:每个像素占用4byte内存 (默认) 
RGB_565:每个像素占用2byte内存

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

1.publicstaticBitmapreadBitMap(Contextcontext, intresId) {  
 2.    BitmapFactory.Optionsopt = newBitmapFactory.Options();  
 3.    opt.inPreferredConfig = Bitmap.Config.RGB_565;  
 4.    opt.inPurgeable = true;  
 5.    opt.inInputShareable = true;  
 6.    //获取资源图片   
 7.    InputStreamis = context.getResources().openRawResource(resId);  
 8.    returnBitmapFactory.decodeStream(is, null, opt);  
 9.}  

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

图片回收:

使用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();  

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

捕获异常:

经过上面这些优化后还会存在报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.}  

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

6.线程优化

线程优化的思想是采用线程池技术,避免创建大量的线程,线程池可以重用内部线程,避免了线程的创建和销毁带来的系统内存开销,同时线程池还能有效的控制线程的最大并发数,避免大量的线程因相互争抢系统的资源而造成阻塞现象的发生,因此在实际开发中,我们要尽量使用线程池,而不是每次都要new Thread

7.修改对象引用类型:使用软引用和弱引用

引用类型:

引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。

强引用(strong reference) 
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

软引用(SoftReference) 
只有内存不够时才回收,常用于缓存;当内存达到一个阀值,GC就会去回收它;

弱引用(WeakReference)

弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

虚引用(PhantomReference) 
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

重点介绍一下软引用和弱引用。

如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。

在java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。 
在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

下面以使用软引用为例来详细说明。弱引用的使用方式与软引用是类似的。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软引用技术来避免这个问题发生。

首先定义一个HashMap,保存软引用对象。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>(); 
 
 
  • 1

再来定义一个方法,保存Bitmap的软引用到HashMap。

public void addBitmapToCache(String path) { 

        // 强引用的Bitmap对象 

        Bitmap bitmap = BitmapFactory.decodeFile(path); 

        // 软引用的Bitmap对象 

        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap); 

        // 添加该对象到Map中使其缓存 

        imageCache.put(path, softBitmap); 

    } 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

获取的时候,可以通过SoftReference的get()方法得到Bitmap对象。

public Bitmap getBitmapByPath(String path) { 

        // 从缓存中取软引用的Bitmap对象 

        SoftReference<Bitmap> softBitmap = imageCache.get(path); 

        // 判断是否存在软引用 

        if (softBitmap == null) { 

            return null; 

        } 

        // 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空 

        Bitmap bitmap = softBitmap.get(); 

        return bitmap; 

    } 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

到底什么时候使用软引用,什么时候使用弱引用呢?

个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

8.资源的回收

  • Thread(线程)回收:
  • Cursor(游标)回收:
  • Receiver(接收器)回收
  • Stream/File(流/文件)回收

9.内存泄漏优化

Android 性能优化之内存泄漏的检测与修复

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值