Andorid性能优化及工作经验

不知不觉做Android已经快4年,早期的开发工作中疲于业务功能的完成,都是怎么快怎么做,在后期遇到一些性能优化的地方,或是因为编码习惯,或是因为数据结构考虑设计不合理,或是简单的UI绘制性能等等都耗费大量的时间进行修补.

不定期更新。。。。。。。

性能优化的点

  • 内存优化
  • 编码习惯,数据结构
  • 网络性能优化
  • UI布局优化
  • 数据库优化
  • 线程优化
  • 电量优化
  • 其他结合业务的经验优化

(1)内存优化

手机的内存根据手机的好坏是不同的,而每个应用可以使用的也是有上限的,当超出临界值时就会产生OOM.

1.1图片相关

  • 一般的图片使用开源的Glide,fresco,Picasso即可解决。

  • 大图片需要自行建立统一工具类使用BitmapFactory获取options来统一对图片进行设备分辨率重采样加载

  • 对图片质量要求不高或者使用RGB_8888还处于OOM的可以选择RGB_565

  • 对于图片的裁剪等操作可以放置到云端让服务器处理,直接加载图片大小的内存,避免不必要的内存消耗

1.2内存泄漏

  • 常见的Handler泄漏,内部类长期持有导致泄漏外部类等可使用WeakReference解决
  • 匿名内部类,单例,静态变量,使用资源后不进行关闭释放(Cursor,BitMap等),Handler等
  • Activity的销毁状态中,如果包含大量ImageView,可以使用递归遍历所有View的ImageView对其进行recycle
  • 某些同学对于内存泄漏喜欢采用android:largeHeap="true"来获取最大内存限制,如果不是必要尽量少用,很多时候其实并不会达到你所申请的内存,对于空余的内存完全是一种浪费消耗
  • 超大背景图等直接使用会在不同分辨率的设备上进行缩放带来多余的内存消耗,尽量对其重采样匹配设备分辨率进行加载

1.3内存管理

Android自身是有一套默认的内存回收机制,根据进程的优先级来进行回收。

  • 进程优先级

Foreground进程>Visible进程>Service进程>Background进程>Empty进程

  • AMS管理进程的内存资源分配
  • 堆内存越大,GC时间越长
优化
  • 有些同学滥用service,在没有任何任务时也常驻,建议在空闲时将service关闭
  • 使用support-annotations替代枚举实现方式
  • 通过ActivityManagergetMemoryClass或者getLargeMemoryClass来获取内存控制应用内存值小于可使用的内存值
  • 对于非必要实例对象不要疯狂胡乱创建(消耗16字节)
  • 可使用onTrimMemory做相应的内存控制

(2)编码习惯,数据结构

编码习惯

直接参考阿里巴巴的Java编码规范,阅读完后相信你会大有收获.

2.1数据结构

  • ArrayList、LinkedList、Vector
    • ArrayList属于动态数据结构,LinkedList属于链表结构

    • 数据量小的情况小,随机查找get,set某一个值,两者区别不大

    • 大量数据时get或者set,ArrayList优与LinkedList,除非从两端查找则LinkedList优于ArrayList

    • LinkedList新增或者删除数据优于ArrayList,除非ArrayList删除或者增加是在末尾

    • Vector类似ArrayList,内部方法进行了同步保证线程安全,效率相对ArrayList稍低

ArrayList适用于数据随机的的get,set**

LinkedList适用于数据的add,remove

Vector保证线程安全,效率稍低

  • HashMap、HashSet、Hashtable、TreeMap
    • HsahMap存储键值对,HashSet直接存储对象

    • HashSet内部包含HashMap,不允许重复的元素

    • 多线程中使用HashMap需要Collections.synchronizedMap来进行同步,而Hashtable可直接使用

    • HashTable不能有空的key,value

    • HashMap使用了Iterator,而HashTable使用Enumeration

    • 在使用Collections.synchronizedMap过程中,因为加锁为对象,并发性差

    • TreeMap不能为null且数据不重复,可用于大小排序

(3)网络优化

网络的优化在App中也是很重要的一点,因个人对网络协议的研究也不深,此处的总结主要为C端在工作中的开发经验总结。

网络对用户产生以下问题

  • 电量
  • 流量

3.1优化

3.1.1减少网络请求的次数,资源状态

例:注册与登录模块,注册后的直接登录,常规需要C端调用注册后再继续调用登录,对于登录的这一流程可以结合业务直接让服务器完成.

3.1.2 使用GZip来对Body进行压缩减request和response
3.1.3 对于大量的全量数据传输,建议采用protobuf
3.1.4 图片的获取直接利用云端处理获取精准的图片大小,质量,格式等(类似7牛)
3.1.5 对网络数据进行缓存处理

例:Retrofit2,OkHttp3中可直接创建cache进行缓存

 OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
  builder.cache(
      new Cache(context.getCacheDir(), CACHE_SIZE_BYTES));
  • 与服务器约定特定的值,自行处理API请求结果缓存

  • 通过If-Modified-SinceLast-Modified

If-Modified-Since作为请求头标签,第一次获取200状态,服务器返回Last-Modified,再次发送HTTP请求时,将服务器返回的Last-Modified发送,服务器获取判断数据是否已经更改,没有更改则直接返回304处理,数据改变则重复上述流程

  • Splash广告一般是预下载,下次进入的时候在进行展示
3.1.6 C端对服务端协商契约

客户端与服务端进行契约,在处理网络时,对非正常数据抛到外层进行统一解析,避免无效使用,同时利于后期维护迭代

3.1.7 对设备进行监听结合一些特殊业务进行网络打包

对于特定的一些批量业务下载或者一个页面多个请求接口,如Splash页面缓存,皮肤下载等等,可以将其打包利用JobScheduler在空闲时间,充电并且是WIFI网络下,或者用户设置的时间进行下载处理.

3.1.8 弱网无网优化
  • 弱网环境下使用缓存数据
  • 弱网环境图片不进行加载

在弱网环境甚至无网环境,某些特定业务为了提高体验或者说提高产品业绩,可优先对界面进行反馈,操作流程数据可保存下来,监听网络连接状态,后续使用JobScheduler在强网状态下进行发送处理(或者保存在数据库中,新开App时对数据库进行检测发送)

(4)UI优化

  • XML布局中,对于多状态的布局尽量采用ViewStub标签
  • 若父节点与子节点为同一类型,采用merge标签
  • 高复用的布局,使用include
  • 善于利用最新的布局ConstraintLayout,减少布局层次
  • 移除不必要的BackGround

(5)数据库优化

  此块个人优化接触较少,缺省....后续补充

(6)线程优化

  • 单线程创建只需要使用Thread类或者Runnable即可,任务结束后会自动回收
    • Runnable接口的线程可以处理公共资源,而Thread只能单独负责自身的资源
  • 多线程中线程频繁的创建销毁消耗资源性能,可能导致内存抖动从而导致界面的卡顿现象
  • 利用ThreadPoolExecutor来优化管理线程【Bugly干货分享】Android性能优化典范之多线程篇

建议微信关注公众号腾讯Bugly,经常有意想不到的干货分享

(7)电量优化

  • 避免空闲服务的运行
  • 减少网络的请求链接
  • 对服务类产品的定位需求,若不需要精准GPS定位,使用网络定位即可
  • 对特殊业务的预处理上传等,使用JobScheduler可定制于电量>X时在进行网络请求

其他经验

个人以及技术上


  • Fragment/ViewPager等尽量做预加载和懒加载
  • 利用Fragment对业务进行模块化
  • 平行层级的模块业务可用组件化或者插件化出来
  • 多个项目组可复用的业务可队形进行SDK模块化
  • 随着学习的加深,多锻炼抽象思维与设计模式的使用
  • 对用户行为进行分析,预判行为加载数据
  • 图片加载先加载小图展示,在加载大图显示
  • 对App进行数据分析监控,如Crash,ANR等进行上报
  • Debug调试过程中尽量多打LogDebug,Release时应关闭不要的Log
  • 空闲可学习后端,前端的知识,以及RN,WEEX等
  • 若发现自己知识的广度不能扩展,就去抓知识的深度
  • 可以不看文档,不看博客,但是需要建立一套适合自己的学习模式或者学习笔记
  • 初学或者不理解的知识,不用明白原理,照抄一边,在实现之后快速阅读理解
  • 谁也不能保证所有知识一直在脑袋里,遗忘之后可偶尔看一看以前的笔记或者别人的博客文档等
  • 尽量能做的事不拖到下班之后,下班之后学会享受生活,该加班时也没办法
  • 有条件可做快速开发工具,提高工作效率

团队上


  • 项目组对代码进行一定风格的统一,若不能统一在基本的地方尽量标准(具体看Leader的处事风格)

  • 慎用第三方控件,使用之前优先看issue来调研决定是否使用,遇到问题也第一时间查找issue有无类似问题

  • 迭代维护开发项目,功能开发之前尽量做足调研,报告在进行开发

  • 当有应届生时,应安排有耐心的的而非能力强的老员工进行Code-Review,当然能力强有责任心更好

  • 若大多老员工都无耐心,对应届生的工作,可为期提供一套模板代码,让期阅读在进行提问考核

  • 第一版项目少做思考性能,优化,代码质量的问题,优先做出来,发展到一定程度在决定(当然也不能太差,个人风格)

  • 团队内拒绝批判他人代码质量是对一个人付出的尊重(可以内心鄙视不屑,至少明面上不能说出来),允许的情况下可分享提高,最终在做下一步结论(考察,踢出团队等)

  • 关于重构

    • 拒绝动不动就重构项目,重构的前提建立在对业务熟悉的情况下,个人认为业务稳定性大于代码质量
    • 决定重构时,制定计划标准,重构时更能对业务有清晰的分析定位,建立更好的生态
  • 若你是Leader

    • 技术或许并不是你的强项,但应具备遇事沉稳冷静,善于听取团队成员的意见,有承担力,必须为团队的犯错买单背锅(哪怕是成员的错误也应帮他买单,同样的错误再犯则由团员自行承担并惩罚)
    • 做事不能完全从技术角度出发,更多的参与到产品设计运营等讨论中
    • 尽量不要为自己分配具体的需求任务,应更多的关注团队成员的工作状态以及任务的分配
    • 具有一定的前瞻力或者经验,对于耗时耗力的任务尽早分配
  • Leader的工作

    • 分配Task到具体的成员
    • 组织修复Crash崩溃以及突发状况等
    • 架构组与业务组对接过程中,优先安排架构组的任务
    • 对当前任务计划的列表安排统计,预留一定的时间
    • 有条件可以组建Code-Review
    • 每周或者两周进行例会
  • Android/iOS中耗时耗力的任务

    • Android/iOS的打包任务
    • 架构组的做架构时,安排业务的人进行技术调研
    • 对某一技术可实施性的调研任务分配

选择比努力重要,性格态度比能力重要

推荐阅读更多精彩内容