V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kaedea
V2EX  ›  Android

[收集向] Android 性能调优的技术点

  •  8
     
  •   kaedea · 2015-11-07 03:42:28 +08:00 · 6377 次点击
    这是一个创建于 3087 天前的主题,其中的信息可能已经有所发展或是发生改变。

    神马情况

    最近一个星期居然没有产品的需求,本来打算涂几个妹子练练手,没想到老大让我把“性能优化”总结一下,输出一个文档,给那些新来的小伙子看看,免得重复踩坑。这件事我 1 年前就想搞了,也是时候好好梳理一下。

    其实性能调优涉及到多方面的工作,一晚上也只能想到这么多,接下来有时间的时候我打算把以下的每一点都讲详细一点,配合实际情景,再写个 DEMO 之类。

    欢迎大家补充要点啊,我一并处理。

    要点

    使用异步

    • 保持 APP 的高度响应,不要在 UI 线程做耗时操作,多使用异步任务
    • 使用线程时要做好线程控制;使用队列、线程池
    • 谨慎使用糟糕的 AysncTask 、 Timer
    • 警惕异步任务引起的内存泄露
    • 应该异步任务分类,比如 HTTP ,图片下载,文件读写,每一类的异步任务维护一个任务队列,而不是每一个任务都开一个线程( Volley 表示我一个可以搞定这些全部 _(:з」∠)_)
    • 这些常用的任务应该做好优先级处理(一般 JSON 数据优先于图片等静态数据的请求)
    • 一般异步任务应该开启一个 SingleAsyncTask ,保证一时只有一个线程在工作
    • HTTP 和图片下载尽量使用同一套网络请求
    • 使用 MVP 模式规范大型 Activity 类的行为,避免异步任务造成的内存泄露

    避免内存泄露

    • 了解虚拟机内存回收机制
    • 频繁 GC 也会造成卡顿,避免不必要的内存开销
    • 错误的引用姿♂势造成的内存泄露(啊~要泄了~)
    • 常见的 Activity 泄露(单例、 Application 、后台线程、无限动画、静态引用)
    • Bitmap 泄露( HoneyComb 这个问题之前压力好大)
    • 尽量使用 IntentService 代替 Service ,前者会自动 StopItself
    • 排查内存泄露问题的方法(我一直以来都是简单暴力的人肉 dump 检查大法)
    • 使用 LeakCanary 自动检查 Activity 泄露问题
    • 对内存负载要保持敏感( Sharp )

    视图优化

    • 布局优化、减少层次, Include Merge
    • 使用 ViewStub 避免不必要的 LayoutInflate ,使用 GONE 代替重复 LayoutInflate 同一个布局
    • 避免过度绘制,应该减少不必要的布局背景;布局层次太深会造成过度绘制以及 Measure 、 Layout 等方法时间复杂度的指数增长
    • 使用过渡动画,比如给图片的呈现加一个轻量的淡入效果会让视觉上变得流畅许多
    • 避免过度的动画,不要让一个界面同时出现多出动画,比如 List 滚动时 Item 项要停止播放动画或者 GIF
    • 复杂动画使用 SurfaceView 或 TextureView
    • 尽量提供多套分辨率的图片,使用矢量图

    Adapter 优化

    • 复用 convertView ,用 ViewHolder 代替频繁 findViewById
    • 不要重复 setListener ,要使用 v.getId 来复用 Listener ,不然会创建一堆 Listener 导致频繁 GC
    • 多布局要采用 MutilItemView ,而不是使用一个大布局然后动态控制需要现实的部分
    • 不要在 getView 方法做做耗时的操作
    • 快速滚动列表的时候,可以停止加载列表项的图片,停止列表项的动画,不要在这时候改变列表项的布局
    • 尽量用 RecyclerView (增量 Notify 和 RecycledViewPool 带你飞)

    代码优化

    • 算法优化,减少时间复杂度,参考一些经典的优化算法
    • 尽量使用 int ,而不是 float 或者 double
    • 尽量采用基本类型,避免无必要的自动装箱和拆箱,浪费时间和空间
    • 选用合适的集合类(尽量以空间换时间)、选用 Android 家的 SparseArray,SparseBooleanArray 和 LongSparseArray
    • 避免创建额外的对象( StringBuilder )
    • 使用 SO 库完成一些比较独立的功能(高斯模糊)
    • 预处理(提前操作)一些比较耗时的初始化工作统一放到启动图处理
    • 懒加载(延迟处理)规避 Activity 的敏感生命周期
    • Log 工具类,要在编译时删掉调试代码,而不是在运行时通过判断条件规避
    • 优先使用静态方法、公有方法还是公有方法?速度区别很大哦
    • 类内部直接对成员变量进行操作,不要使用 getter/setter 方法,调用方法耗额外的时间
    • 给内部类访问的外部类成员变量要声明称包内可访问,而不是私有,不然编译的时候还是会自动创建用于访问外部类成员变量的方法
    • 遍历集合时,使用 i++代替 Iterator ,后者需要额外的对象操作,应在循环体内避免这种情况
    • 如果一个基本类型或者 String 的值不会改变,尽量用 final static ,编译时会直接用变量的值替换变量,也就不需要在查询变量的值了

    其他优化

    • 数据库优化:使用索引、使用异步线程
    • 网络优化 …… 一堆优秀的轮子
    • 避免过度使用依赖注入框架,大量的反射
    • 不过过度设计 /抽象,多态看起来很有设计感,代价就是额外的代码、空间、时间
    • 尽量不要开启多进程,进程的开销很大

    APK 瘦身

    • 开启混淆
    • 使用 zipalign 工具优化 APK
    • 适当有损图片压缩、使用矢量图
    • 删除项目中冗余的资源,之前写过一些删除没有 res 资源的脚本
    • 动态加载模块化,项目拆分啊!

    性能问题的排查方法

    • GPU 条形图,没事开来看看淘宝
    • 过度绘制颜色,嗯,不要一篇姨妈红就好
    • LeakCanary ,自动检测 Activity 泄露,挺好用的
    • TraceView ( Device Monitor ), Systrace ,分析哪些代码占用的 CPU 时间太大,屡试不爽
    • Lint ,检查不合理的 res 资源
    • layoutopt (还是 optlayout ?),对当前布局提出优化建议,已被 lint 替代,但是还能用
    • HierarchyViewer ,查看手机当前界面的布局层次,布局优化时常用(只用于模拟器,真机上用要 ROOT ,不想 ROOT 加得使用 ViewServer )
    • StrictMode , UI 操作、网络操作等容易出现性能问题的地方,如果出现异常情况 StrictMode 会报警

    欢迎各位补充

    15 条回复    2015-11-15 18:26:15 +08:00
    greenskinmonster
        1
    greenskinmonster  
       2015-11-07 08:05:16 +08:00
    不错,挺全面的样子。先感谢下,慢慢研究。
    suikator
        2
    suikator  
       2015-11-07 08:28:54 +08:00 via Android
    感谢分享,以前一直没注意 listener 复用问题
    sandideas
        3
    sandideas  
       2015-11-07 08:34:25 +08:00 via iPhone
    发现有一大半听不懂。。
    不过听懂的但是觉得意见很给力。。
    我也是一直没注意到 listener 复用问题,一般都是心情好复用,心情不好就直接 new 一个 listener
    indepead
        4
    indepead  
       2015-11-07 09:43:16 +08:00 via Android
    mark
    olbb
        5
    olbb  
       2015-11-07 09:55:12 +08:00
    不错
    viator42
        6
    viator42  
       2015-11-07 10:36:16 +08:00
    复用 listener 我一般不用,除非很多的元素响应事件一样
    为什么不用 AysncTask?
    getter/setter 算是 java bean 的标准写法,不这么写有可能会出别的问题.
    zhouquanbest
        7
    zhouquanbest  
       2015-11-07 12:43:17 +08:00
    一些老生常谈的东西 我觉得归位好的开发规范和习惯比较好
    allenx
        8
    allenx  
       2015-11-07 13:03:52 +08:00
    JNI 代码优化呢, android 下 native code 如何 profiling
    Zxw
        9
    Zxw  
       2015-11-07 13:29:19 +08:00
    学习了
    shakespaces
        10
    shakespaces  
       2015-11-07 16:12:43 +08:00
    错误的引用姿♂势。。。
    AtlantisZ
        11
    AtlantisZ  
       2015-11-07 16:30:42 +08:00 via Android
    很全面
    baitouwei
        12
    baitouwei  
       2015-11-07 17:52:54 +08:00
    @viator42 这还有个问题就是,如果所有的 model 都写 getter/setter ,代码会很容易就超出 65k 限制
    kaedea
        13
    kaedea  
    OP
       2015-11-08 00:49:31 +08:00
    @baitouwei 现在大部分项目都是开 MutilDex 解决这个问题,之前为了缓解这个问题,我们也研究过一些减少方法数的办法,嘛,治标不治本,给方法数共享最多的都是 Library
    Froyo9
        14
    Froyo9  
       2015-11-10 16:10:57 +08:00
    谢谢分享
    drakeet
        15
    drakeet  
       2015-11-15 18:26:15 +08:00 via Smartisan T1
    @viator42 官方已经不推荐 get set 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2927 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 03:00 · PVG 11:00 · LAX 20:00 · JFK 23:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.