[收集向] Android 性能调优的技术点 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kaedea
V2EX    Android

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

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

    神马情况

    最近一个星期居然没有产品的需求,本来打算涂几个妹子练练手,没想到老大让我把“性能优化”总结一下,输出一个文档,给那些新来的小伙子看看,免得重复踩坑。这件事我 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
    img src="https://cdn.v2ex.com/avatar/37f8/cd10/54640_normal.png?m=1392693305" class="avatar" border="0" align="default" alt="olbb" data-uid="54640" />
        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     Solana     1330 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 16:57 PVG 00:57 LAX 09:57 JFK 12:57
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86