Android性能优化实战总结

1. 写在前面

1.1 性能优化的工具

  • Android开发者选项
    重点说下Profile GPU Rendering,实时反应当前每一帧的耗时,可用于分析卡顿的地方。
  • 分析具体耗时的Trace view以及整体情况的Systrace
    Systrace用于对特定时间段内如UI或动画的整体情况,而Trace view用于对性能分析进行说明,可以分析到每一个方法的执行时间,可以统计出该方法的调用次数和递归次数,CUP占用百分比等。
  • 布局复杂度工具Hierarchy View
    可以查看布局的层级,Measure,Layout和Draw的对应耗时。
  • 应用启动时间
    // 测试Activity启动时间,如MainActivity,APP启动时间
    adb shell am start -W packagename/activity
    
  • 静态代码检查工具Lint
  • 程序稳定性monkey
  • 使用LeakCanary检测内存泄露

1.2 优化的一些点

1.2.1 布局优化

  • 使用FrameLayout等布局,减少嵌套层级,尽量不使用RelativeLayout做根布局,RelativeLayout会让子View调用2次onMeasure,而且布局相对复杂时,onMeasure相对比较复杂,效率比较低,LinearLayout在weight>0时也会让子View调用2次onMeasure。LinearLayout weight测量分配原则。
  • 使用include、ViewStub、merge等标签
  • 使用TextView的leftDrawabel/rightDrawable代替ImageView+TextView布局
  • 注意使用wrap_content,在测量过程中,match_parent和固定宽高度对应EXACTLY ,而wrap_content对应AT_MOST,这两者对比AT_MOST耗时较多。
  • 尽量使用View.GONE取代View.INVISIBLE,或减少使用透明度。
  • 自定义View的优化, onDraw中不用new对象,会引起内存抖动。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
  • 移除xml非必须的背景、移除window中默认的背景、按需显示位图。
  • 减少刷新次数,缩小刷新区域。

1.2.2 加载优化

  • 将耗时的操作封装到异步中去,但多进程情况下,Application的onCreate会被执行多次,注意可能会出现的问题。

  • Json反序列化
    json反序列化是指将json字符串转变为对象,这里如果数据量比较多,特别是有相当多的string的时候,解析起来不仅耗时,而且还很吃内存。解决的方式是:
    a、精简字段,与后台协商,相关接口剔除不必要的字段。保证最小可用原则。

    b、使用流解析,之前我考虑过json解析优化,在Stack Overflow上搜索到这个。于是了解到Gson.fromJson是可以这样玩的,可以提升25%的解析效率。

    public List readJsonStream(InputStream in) throws IOException{
      JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
      List messages = new ArrayList();
      reader.beginArray();
      while (reader.hasNext()){
          Message message = gson.fromJson(reader, Message.class);
          messages.add(message);
      }
      reader.endArray();
      reader.close();
      return messages;
    }
    

1.2.3 内存优化

  • 加载大图时按需加载,合理选择inSampleSize,使用LruCache缓存。
  • 使用线程池管理线程。

1.2.4 内存泄露优化

  • 资源性对象未关闭释放。如Cursor、File等。
  • 注册事件的注销,如广播、事件注册后未注销、观察者列表维持着对象的引用。
  • Handler泄露。
    private static class MyHandler extends Handler { 
    private final WeakReference mActivity; 
    // ...
    public MyHandler(MainActivity activity) {
      mActivity = new WeakReference(activity);
      //... 
    }
    
    @Override
    public void handleMessage(Message msg) { 
    }
    //...
    }
    
  • 尽量使用静态内部类。
  • 在Callback里打印log。

2. 卡顿优化

2.1 卡顿的场景

卡顿的场景可以从UI(绘制、刷新)启动速度(冷热启动)跳转(页面间,前后台)响应(按键、系统事件、滑动)四个方面去分类,主要原因还是以下两种:

  • 界面绘制
    绘制的层级深度、页面复杂度、刷新不合理。
  • 数据处理·
    数据在UI线程处理、数据处理占用CPU过高、内存分配不合理导致频繁GC。

2.2 启动速度

启动速度的话,可以使用adb命令查看启动时间。

// 测试Activity启动时间,如MainActivity,APP启动时间
adb shell am start -W packagename/activity

冷启动黑白屏问题,可以通过设置theme设置background来解决;冷启动与热启动速度问题,需要优化UI线程的处理逻辑。

2.3 稳定性优化

  1. 采用TDD开发模式,提高单元测试覆盖率,方法的测试覆盖率。
  2. 使用Android Lint等静态工具,提高程序的稳定性。
  3. 使用Monkey测试等工具,减少隐藏的闪退。
adb shell monkey -p com.your.packagename -v 10000
  1. 使用bugly等Crash监控上传工具,便于持续提升程序的稳定性。

3. APK瘦身

1. 开启shrinkResources去除无用资源

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
        }
    }
}

shrinkResources依赖于minifyEnabled,就是打开shrinkResources也必须打开minifyEnabled。

2. 借助Android Lint删除未使用的资源代码

Analyze -> Run Inspection by Name -> unused resources -> Moudule ‘app’ -> OK
Analyze -> Run Inspection by Name -> unused declaration -> Moudule ‘app’ -> OK

3. 压缩图片或使用webp格式图片
使用pngquant优化png图片的大小

// pngopt.sh
#! /bin/bash 
set -eux
find . -name '*.png' -print0 | xargs -0 -P8 -L1 pngquant --ext .png --force 256

4. 使用插件化
插件化可以将APK的体积减小,在用户需要使用到相关模块后,再从服务器进行下载。

5. 向量图优化(去除向量图小数点)
使用\.\d+去除向量图中的小数点,可以编写一个python脚本全局执行,代码较多,我这里就不展示了。

4. 省电流量优化

  1. 使用Battery Historian 系统电量分析工具,分析耗电过程,优化APP耗电。
    一般耗电的场景有:
    • 计算优化,减少或避开浮点运算等。
    • 避免wakelock的使用不当,屏幕唤醒是耗电最厉害的,特别注意。
    • 使用Job Scheduler。
  2. 使用GPS定位耗电,但定位进准,如果要求不那么进准的话,可以使用wifi/移动网络进行定位。
  3. 网络差的情况下,减少一些比不要的请求加载,如加载一张大图等,另没有wifi/蓝牙可连的情况下,尽量关闭。
  4. 流量优化的话,只能说尽量减少不必要的请求了,减少下载,比如使用缓存。

总结:其实一定程度上,严格按照代码规范来执行,很大程度上就能避免很多问题,上面的这些性能优化措施,只能是后面出现问题的解决方案,重要的是我们要去学会编写高质量的代码,从而避免这些问题的出现。

更多参考

你可能感兴趣的:(Android性能优化实战总结)