美文网首页
Android App性能优化

Android App性能优化

作者: 晨树Tec | 来源:发表于2019-01-10 15:43 被阅读0次

    随着移动应用在中国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可能会让你得不偿失。

    相关文章

      网友评论

          本文标题:Android App性能优化

          本文链接:https://www.haomeiwen.com/subject/ncyarqtx.html