Android App性能优化

随着移动应用在中国6、7年的发展,手机应用的开发已经很完善了,如果boss让你开发一个应用,你会发现你要开发的功能在公司里面基本都能找到类似的实现代码,就算在公司找不到,在网上也有大把的demo,这样程序开发的门槛就降低了,无非是代码的解读与拼凑(仅在实现功能的角度来说),所以为了成为一个优秀的程序员,大家就不能仅满足于实现功能,更重要的是写出优秀的程序。在这篇文章里,我会结合过去的开发经验—特别是图库的开发,谈谈对于Android性能优化的个人见解。

一、            概述

概括来说,我认为Android应用性能优化包括如下几方面:

内存 、线程、流程逻辑、数据结构、逻辑算法、系统特性、编程语言

1.        内存优化

开发过程中内存优化涉及到如下区域:

寄存器(Register):速度最快,位于服务器内部,一般我们无法控制

栈(Stack) :存放基本数据类型与指向对象的指针的地方

堆(Heap):存放对象的地方,受GC管理

 

Android 应用有内存大小限制,超过了会引发oom,具体的限制值可以在/system/build.prop中查看:

   -dalvik.vm.heapstartsize 表示默认分配的内存

-dalvik.vm.heapgrowthlimit   表示默认情况下内存的最大允许值

-dalvik.vm.heapsize当我们在AndroidManifest.xml中设了largHeap = true时,内存的最大允许值。   

当然我们在开发的时候也可以通过Runtime.getRuntime().maxMemory();取得当前最大允许的内存值。

 

5R:

内存的优化工作也可以从以下5个方面思考。

1.Reckon(计算)

首先需要知道你的app所消耗内存的情况,知己知彼才能百战不殆

2.Reduce(减少)

消耗更少的资源

3.Reuse(重用)

当第一次使用完以后,尽量给其他的使用

5.Recycle(回收)

返回资源给生产流

4.Review(检查)

回顾检查你的程序,看看设计或代码有什么不合理的地方

 

1.        

1.1.       充分使用缓存机制

我们的应用并不是使用内存越少越好,相反充分的使用内存,能有效的提高效率,chrome浏览器占用内存明显比较高,一定程度上也可以看做内存换效率。

以从网上加载一张图片为例:

 

 

 

其中涉及到图片下载、图片保存、图片解码、内存管理、图片信息管理。

     在文件下载到ram,以及保存到disk中,比较好理解,当然把图片的id用数据库保存起来, 记得close数据流,防止内存泄漏。至于内存中的缓存,我们要在应用内存大小的限制之内,尽量高效率的复用bitmap,以前我们用软引用/弱引用的机制来保存图片对象,然而Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,这使得上述的方案相当无效。幸好Google给了我们Lrucache,它将被引用的对象保存在LinkedHashMap中,并且当缓存超过指定大小后,释放最不常使用对象。

 

关于图片缓存,我们有很多开源项目:

Glide,(首选)Android-Universal-Image-Loader 图片缓存、picasso square开源的图片缓存、ImageCache图片缓存,包含内存和Sdcard缓存。

 

1.2.       尽量不使用全局变量

首先这里不包括static final的基本数据类型。

处女座的老板可能会要求你完全消灭全局变量。全局变量不单会一直占据内存资源,关键是内存不足的情况下,系统会回收一部分资源,由于APP切换到后台,所以之前的全局变量可能被回收,这样应用面临一个很不确定的风险。

要解决这个问题,建议:

1)      把全局变量序列化之后保存到本地。

2)       把变量放到Application中声明,除非应用退出,否则Application不会被回收,作为Application的成员变量也就不会被回收,同时,在应用内部Application的成员变量的作用域也类似于全局变量了。

1.3.       提防内存泄漏,比如context被长于Activity的声明周期的对象引用,bitmap要recycle,数据流要close,数据库要close,广播要unregister

 

1.4.       按需加载图片

1) 通过设置BitmapFactoryOption.sampleSize 修改加载的图片大小而不是加载了图片之后再缩放。

2) 设置图片像素的质量。分别是:

          ALPHA_8每个像素占用1byte内存 (只有透明度)
ARGB_4444每个像素占用2byte内存  (有透明度但没那么细腻)
ARGB_8888每个像素占用4byte内存(默认)
RGB_565每个像素占用2byte内存 (没有透明属性)

3) 通过计算当前应用允许的最大占用内存值,决定缓存的大小(可以在LruCache中设定),一般是最大值的1/8,按应用实际需求决定。

 

1.5  inBitmap

BitmapFactory.Option.inBitmap

如果设置了这个字段,bitmap在加载数据时可以复用这个字段所指向的bitmap的内存空间。

新增的这种内存复用的特性,可以优化掉因旧bitmap内存释放和新bitmap内存申请所带来的性能损耗。

一般把inBitmap指定为可回收的bitmap,已达到复用这个bitmap的空间的目的

需要注意的是inBitmap只能在3.0以后使用。在4.4之前,只能重用相同大小的bitmap的内存区域,而4.4之后你可以重用任何bitmap的内存区域,只要这块内存比将要分配内存的bitmap大就可以

 

1.6 使用NDK

由于JVM中java内存堆和Native空间的内存堆是独立的,我们一般在java空间开发比较多,内存空间比较吃紧,可以把部分逻辑放到native空间。

2.        线程优化

般的应用都会涉及到多线程吧,特别是耗时逻辑放在主线程容易引起anr,很影响用户体验(为了让屏幕的刷新帧率达到 60fps,我们需要确保 16ms 内完成单次刷新的操作,一旦刷新帧率降到 20fps 左右,用户就可以明显感知到卡顿不流畅了)。我们应该关注的怎样合理使用、管理多线程。

1)    选择适当的工具

AyncTask AsyncTask 提供了一种简单便捷的异步机制,适合简单任务

HandlerThread HandlerThread 比较合适处理那些在工作线程执行,需要花费时间偏长的任务

IntentService IntentService 就不仅仅具备了异步线程的特性,还同时保留了 Service 不受主页面生命周期影响的特点

2)    关注线程的并发数

通常核心线程数设为CPU数量+1

最大线程数设为CPU数量*2+1

获取CPU数量的方法Runtime.getRuntime().availableProcesses():获取活动的cpu的数量

sys/devices/system/cpu 获得真实cpu的核数

3)    关注线程的优先级

  对于Android平台上的线程优先级设置来说可以处理很多并发线程的阻塞问题,比如很多无关紧要的线程会占用大量的CPU时间,虽然通过了MultiThread来解决慢速I/O但是合理分配优先级对于并发编程来说十分重要。

  我们可以通过Process设置线程优先级:比如

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

创建ThreadPoolExecutor时可以传入new PriorityThreadFactory("thread-pool",android.os.Process.THREAD_PRIORITY_BACKGROUND)

具体Thread的优先级包括:

Int THREAD_PRIORITY_AUDIO //标准音乐播放使用的线程优先级

int THREAD_PRIORITY_BACKGROUND //标准后台程序

int THREAD_PRIORITY_DEFAULT // 默认应用的优先级

int THREAD_PRIORITY_DISPLAY //标准显示系统优先级,主要是改善UI的刷新

int THREAD_PRIORITY_FOREGROUND //标准前台线程优先级

int THREAD_PRIORITY_LESS_FAVORABLE //低于favorable

int THREAD_PRIORITY_LOWEST //有效的线程最低的优先级

int THREAD_PRIORITY_MORE_FAVORABLE //高于favorable

int THREAD_PRIORITY_URGENT_AUDIO //标准较重要音频播放优先级

   intTHREAD_PRIORITY_URGENT_DISPLAY //标准较重要显示优先级,对于输入事件同样适用。

推荐使用RxJava 2.x,内部使用了线程池,并能灵活切换线程、配置线程优先级。

PS:

经典组合RxJava(多线程流式调用)+Retrofit(基于注解的请求框架)+OkHttp(高效网络框架)能解决大多数app需求

3.         流程逻辑

流程逻辑的优化比较宽泛、灵活。

举个例子:

一个图片社交软件,最基础也是最基本的图片展示功能,当然可以用简单的方式从后台拉到图片就展示出来,但为了更好的网络体验,可以大概如此从流程上作优化:

如上图,实际上是把一个简单的事情精细化、复杂化,基本的原理是从流程逻辑上左优化,从而为性能/用户体现服务。

 

4.        数据结构

  一方面是了解已有的工具类的内部原理,一方面是自定义类要注意性能。

比如:

  • 尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销
  • 尽量用SpareArray代替HashMap
  • 多用System.arraycopy、String.indexOf()、String.lastIndexOf()等使用c底层实现的方法,代替自己写循环
  • 对于频繁I/O,推荐使用高性能的MemoryFile类

 

5.        逻辑算法

一般的Android App处理的数据量是比较少的,所以对算法优化的需求比较少,但我们也不能完全排除算法优化上的需求。比如从后台来回来的数据量特别多,统计处理的本地Log特别多,我们就要考虑到算法优劣的问题。

6.        系统特性

  • 某些配置,会对我们的app性能有影响。比如:

android:hardwareAccelerated 可以在Application、Activity、Window、View四个级别进行硬件加速控制,硬件加速执行的所有的绘图操作都是使用GPU在View对象的画布上来进行的,但需注意它会使app占用更多的内存资源,以及对自定义的view和drawable未必能完全支持。

android:largeHeap 可以让App可使用的内存堆更大,app的确需要更多内存的时候可以配置,比如图库。

  • layout布局性能对比:

ConstraintLayout>RelativeLayout>LinearLayout

一般来说ConstraintLayout的性能是最好的,这主要是得益与它使用了Cassowary算法。

7.        编程语言

Android App编程中可能会用到Java、C/C++(Dart暂且不说)。由于C/C++可以直接操作内存空间,所以效率是比Java高,Google也为我们提供了调用C/C++的方式:JNI,但需要注意,使用jni本身就有一点的资源消耗,如果不是对性能有要求的操作,使用jni可能会让你得不偿失。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值