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大就可以

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的确需要更多内存的时候可以配置,比如图库。

7.        编程语言

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容