《Android开发艺术探索》学习笔记之Android的性能优化

一、常见性能问题

1、ANR
  • ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出如图那样的ANR对话框.
    image

    • 产生原因:在主线程(UI线程)里面做了太多的阻塞耗时操作,例如文件读写, 数据库读写, 网络查询等等.
      • 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等)
      • BroadcastReceiver在10s内无法结束

    参考文章:App优化之ANR详解: https://www.jianshu.com/p/6d855e984b99

2、OOM
  • OOM全称Out of Memory,也就是内存溢出
    • 内存泄漏会导致内存溢出

二、Android的性能优化方法

1、布局优化
  • 思想:尽量减少布局文件的层级

  • 方法:有选择的使用ViewGroup

    • 性能较低:RelataiveLayout(需要处理相对定位)
    • 简单高效:FrameLayout和LinearLayout
  • 通过标签简化:

    • < include >
    • < merge >:一般与搭配使用
    • < ViewStub>
      • 继承了View,轻量级,宽高都是零
      • 本身不参与任何布局和绘制过程
      • 意义在于按需加载所需的布局文件(使用时再加载)
      • 当ViewStub被加载后就会被其内部的布局替换掉,此时ViewStub就不再是整个布局结构中的一部分了
        • 加载的两个方法
        ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
        
        View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
        
      • 目前ViewStub还不支持
2、绘制优化
  • 思想:View的onDraw方法中要避免执行大量的操作

  • 方法:

    • (1)onDraw()中不要创建新的局部对象
      • 因为onDraw方法可能会被频繁调用,这样在一瞬间可能会产生大量的临时对象
    • (2)onDraw方法中不要做耗时的操作
      • 要避免大量循环导致CPU时间片被抢占,出现绘制不流畅的情况
      • 官方优化标准帧率:60fps
        • 即每帧的绘制时间不超过16ms(16ms = 1000ms / 60)
3、内存泄漏优化
  • 思想:

    • 一方面是在开发过程中避免写出内存泄漏的代码
    • 另一方面是通过一些分析工具比如MAT来找出潜在的内存泄漏继而解决
  • 场景1:静态变量导致的内存泄漏

    • 静态持有因为其使用的生命周期不一致而导致内存泄露
      • 改进:可以在适当的时候讲静态量重置为null,使其不再持有引用,这样也可以避免内存泄露
    • 静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放
    //Info内部持有了当前的Activity,所以Activity仍然无法释放
    
    public class MainActivity extends AppCompatActivity {
    
        private static Info sInfo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sInfo != null) {
                sInfo = new Info(this);
            }
        }
    }
    
    class Info {
        public Info(Activity activity) {
        }
    }
    
  • 场景2:单例模式导致的内存泄漏

    • 单例模式因为其生命周期与Application保持一致而常常导致内存泄漏
    public class AppSettings {
    
        private static AppSettings sInstance;
        private Context mContext;
    
        private AppSettings(Context context) {
            this.mContext = context;
        }
    
        public static AppSettings getInstance(Context context) {
            if (sInstance == null) {
                sInstance = new AppSettings(context);
            }
            return sInstance;
        }
    }
    
    • 在调用getInstance(Context context)方法的时候传入的context参数是Activity、Service等上下文,就会导致内存泄露

      • 在获取单例时传入的Context的生命周期与单例的生命周期不一致,当退出Context时,该Context就没有用了,但是单例仍然持有对Context的引用,导致此Context无法正常回收,从而导致内存泄漏
    • 改进:将引用的生命周期改到和单例模式一样长

      • eg:可以将context参数改为全局的上下文
        //全局的上下文Application Context就是应用程序的上下文
        //和单例的生命周期一样长
        
        private AppSettings(Context context) {
            this.mContext = context.getApplicationContext();
        }
        
  • 场景3:属性动画导致的内存泄露

    • 属性动画中有一类无限循环动画,在Activity中播放此类动画且没有在onDestroy中停止动画,则动画会一直播放下去,无论界面上是否有动画效果。此时Activity的View被动画持有,View又持有Activity,最终导致Activity无法被正常释放
    • 改进:在Activity的onDestroy()中调用animator.cancel()
4、响应速度优化和ANR日志分析
  • 核心思想:避免在主线程中去做耗时操作

  • 出现:

    • 5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等)
    • BroadcastReceiver在10s内无法结束.
  • 定位问题:通过系统在/data/anr目录下创建的文件 traces.txt 分析

    • adb pull /data/anr/traces.txt .
5、ListView和Bitmap优化
6、线程优化
  • 思想:采用线程池
    • 线程池可以重用内部的线程
    • 避免程序中存在大量的Thread
    • 避免了Thread创建和销毁带来的性能开销
    • 避免了大量Thread因为互相抢占资源从而导致阻塞现象发生
7、一些性能优化的小建议
  • 避免创建过多的对象
  • 不要过多的使用枚举
  • 常量请使用static final来修饰
  • 使用一些Android特有的数据结构,比如SparseArray和Pair等,它们都具有更好的性能
  • 适当的使用软引用和弱引用
  • 采用内存缓存和磁盘缓存
  • 尽量采用静态内部类,避免潜在的由于内部类而导致的内存泄漏

三、内存泄漏分析之MAT工具

  • MAT:Eclipse Memory Analyzer

四、提高程序的可维护性

1、命名要规范
2、代码的排版上要留出合理的空白来区分不同的代码块
3、仅为关键代码添加注释
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值