Android下的性能优化

Android下的性能优化

android设备作为一种移动设备,不管是内存还是CPU的性能都受到了一定的限制,无法做到像PC设备那样具有超大的内存和高性能的CPU,鉴于这一点,这也意味着Android程序不可能无限制地使用内存和CPU资源,过多的使用内存会导致程序内存溢出,即OOM,而过多地使用CPU资源,一般指做大量的耗时任务,会导致手机变得卡顿甚至出现程序无法响应的情况,即ANR,由此看来,ANdroid程序的性能问题就变得异常突出了。以下介绍了一些有效的性能优化的方法。

1.布局优化

  • 删除无用的控件和层级。可以使用HierarchyViewer来查看布局层级。
  • 在布局层级相同的情况下,使用LinearLayout的效率比使用RelativeLayout的性能要高,因为RelativeLayout的性能要高,因为RelativeLayout的功能比较复杂,它的CPU渲染时间比较长,而LinearLayout和FragmentLayout都是简单高效的ViewGroup。
  • include标签配合merge标签使用来重用布局、减少布局层数。
  • 使用ViewStub。它是一个非常轻量级的View,宽高都是0,因此它本身不参与任何布局和绘制的过程,它的意义在于按需加载所需要的布局,使用的时候再加载,不需要的时候不加载进来,提高程序初始化的性能,注意:ViewStub与设置visibility之间的差异;设置visibility只是可见,就算设置inVisible或者GONE,布局还是会加载的。

2.绘制优化

  • onDraw方法里不要做初始化对象的操作,因为onDraw方法会被频繁的调用。
  • onDraw方法不要做耗时的操作,这样会造成View的绘制过程不流畅。

3.内存泄漏优化

  • 资源未关闭。使用了broadCastReceiver,ContentObserver,file,Cursor,Stream,Bitmap等资源的使用,应该在Avtivity销毁时及时的关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
  • 静态成员变量持有类的引用。
  • 非静态内部类持有外部类的引用,使用非静态内部类创建的静态变量。
  • 单例引起的内存泄漏。
  • Handler造成的内存泄漏。
  • 线程的生命周期不可控。
  • 无限循环的属性动画引起的内存泄漏。

4.响应速度和ANR日志分析

  • 响应速度优化的核心思想就是避免在主线程中做耗时操作,但是有的时候确实有很多耗时的操作,那就必须将这些耗时的操作放在线程中去执行,即采用异步的方式执行耗时的操作。
  • 在实际开发中,ANR是很难从代码上发现的,如果在开发的过程中遇到了ANR,那么怎么定位问题呢?其实在一个进程发生ANR后,系统会在/data/anr目录下创建一个文件traces.txt,通过分析这个文件就能定位出ANR的原因。

5.Bitmap优化

  • 目前比较常用的缓存策略是LruChache和DiskLruCache,其中LruCache常被用做内存缓存,而DiskLruCache常用做存储缓存。Lru是least Tecently Used的缩写,即最近最少使用算法,这种算法的核心思想为:当缓存快满的时候,会淘汰近期最少使用的缓存目标。
  • 高效加载Bitmap的核心思想,其实很简单,那就是采用BitmapFactory.Options来加载所需的图片尺寸。主要用到了它的inSampleSize参数即采样率。

6.ListView优化

  • 在adapter的getVIew()方法里尽量少使用逻辑;
  • 滑动的时候不要加载图片;
  • 图片的加载使用异步方式;
  • 将listView的scrollingCache和animateCache设置为false. scrollingCache本质上是drawing cache,你可以让一个View将他自己的drawing保存在cache中(保存为一个bitmap),这样下次再显示View的时候就不用重画了,而是从cache中取出。默认情况下drawing cahce是禁用的,因为它太耗内存了,但是它确实比重画来的更加平滑。而在ListView中,scrollingCache是默认开启的,我们可以手动将它关闭。ListView默认开启了animateCache,这会消耗大量的内存,因此会频繁调用GC,我们可以手动将它关闭掉。
  • listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的; 以往我一般都是将listview的高度设置成fill_parent,而这次我是设为wrap_content,这样做的问题在于,ListView没有取到实际的高度,他还要根据计算才能确定,而每一次计算应该会触发listview的渲染,所以就会出现getview的调用次数跟正常情况相比多了好几倍。所以在一般情况下,我建议把listiview在布局文件中的高度总是设置为:fill_parent(或者match_parent),这不仅仅是getview的调用次数问题,还涉及到布局的效率。
  • 使用静态的viewHolder。

7.线程优化

  • 线程优化的思想是采用线程池,避免程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁带来的性能开销,同时线程池还能还能有效地控制线程池的最大并发数,避免大量的线程相互抢占系统资源从而导致阻塞现象的发生。因此在实际的开发中,我们应该尽量的采用线程池,而不是每次都创建一个Thread对象。

8.数据库优化

  • 使用事务。大量的数据的增删如果不使用事务,而是循环调用Insert和Delete的话,会因为每一笔操作都需要打开、写入最后关闭Journal文件(这个文件是临时用来保存数据操作的中间结果)而使得开销非常大。
    使用的方法:
    db.beginTransaction();
    db.setTransactionSuccessful();
    db.endTransaction();
  • 使用索引。索引维护这一个表某一列或者某几列的顺序,这样就可以快速的定位到某一组值,而不是扫描全表。所有的索引信息会被保存到一个独立的索引表中,所以会产生额外的空间占用,不过绝对物超所值,特别是你会在数据库中进行大量的读及搜索操作的时候。
  • sql语句拼接的时候用StringBuffer代替String
  • 在写表的时候调用sqliteOpenHelper&getWritableDatabase(),在读表的时候调用SqliteOpenHelper&getReadableDatabase()
  • 开启子线程进行数据库的读写

9.一些性能优化建议

  • 避免创建过多的对象
  • 不要过多的使用枚举,枚举占用的内存空间要比整形大
  • 常量请使用static final来修饰
  • 使用一些android特有的数据结构,比如SparseArray和Pair等,他们都具有更好的性能
  • 适当的使用软引用和弱引用
  • 采用内存缓存和磁盘缓存
  • 尽量采用静态内部类。这样可以避免潜在的内部类而导致的内存泄漏
  • 尽量使用google推荐的处理方式。比如序列化采用parcelable,而不用Serializable

10.内存优化可能会用到的工具(

  • HierarchyViewer(检查布局层级)
  • TraceView(对应用中方法耗时进行统计)
  • APT(对进程的CPU和内存进行监视和分析)
  • MAT(对内存泄漏分析的主要工具)

11.提高程序的可维护性

  • 提高代码的可读性
  • 灵活应用面向对象语言的三大特性和六大原则从而达成对修改关闭,对扩展开放的终极目标。
坚持原创技术分享,您的支持将鼓励我继续创作!