Android启动优化

APP启动优化

  • 启动方式
      • 冷启动
      • 热启动
      • 温启动
  • 优化方向
  • adb命令获取启动时间
  • 手动埋点计算启动时间
  • 常用的工具
    • traceview
      • 使用方式
    • systrace
      • 使用方式
  • AspectJ工具使用
      • 哪些地方可以hook
      • Join Point (加入切入点)
      • Advice
      • 语法介绍
  • 优化技巧
      • Theme切换
      • 异步优化
      • 启动器
      • 延迟初始化
    • 优化总方针
      • 其它方案

启动方式

冷启动

Android启动优化_第1张图片

热启动

在这里插入图片描述

温启动

  • 会重新走Activity的生命周期
    Android启动优化_第2张图片

优化方向

  • Application和Activity的生命周期

adb命令获取启动时间

  • adb shell am start -W packagename/com.xxx.xxx.首屏Activity
    • ThisTime 最后一个Activity启动耗时
    • TotalTime 所有Activity启动耗时
    • WaitTime AMS启动Activity的总耗时
  • 局限性
    • 只能线下使用,线上无法获取
    • 时间不严谨

手动埋点计算启动时间

  • onWindowFocusChanged Activity的首帧但是数据可能没显示出来
  • 应把数据完全显示出来作为结束时间
public class LaunchTimer {

    private static long sTime;

    public static void startRecord() {   //开始时间
        sTime = System.currentTimeMillis();
    }

    public static void endRecord() {//结束时间
        endRecord("");
    }
// 在界面可见的时候数据显示出来的时候调用,表示app启动完成可用
    public static void endRecord(String msg) { //加tag的结束时间
        long cost = System.currentTimeMillis() - sTime;
        LogUtils.i(msg + "cost " + cost);
    }
}
  • 精确,可以带到上线,推荐使用
  • addOnDrawListerner要求API16

常用的工具

traceview

  • 图形展示执行时间 ,调用栈等信息
  • 信息全面,包含所有线程
  • 代码埋点

使用方式

  • Debug.startMethodTracing(“文件名”);//默认文件大小 8m
  • Debug.stopMethodTracing();
  • 生成文件在手机sd卡 : Android/data/packagename/files
    • Wall Clock Time 代码执行时间
    • Thread Time cpu执行时间
      Android启动优化_第3张图片
  • CallChart
    • 系统api颜色为黄色
    • 第三方库调用的颜色为蓝色
    • 自己的api为绿色
  • Frame chart 火焰图
    • 调用顺序查看
  • Bottom Up
    • 查看api是被谁调用
  • 局限性
    • 运行开销大
    • 可能会对优化造成污染
    • traceview 与 cpu profiler

systrace

  • systraceAPI 18以上,推荐使用TraceCompat 向下兼容的类

使用方式

  • 添加代码
TraceCompat.beginSection("AppOncreate");
...
TraceCompat.endSection();
  • python systrace.py -t 10 [other-options] [categories]
  • https://developer.android.com/studio/command-line/systrace#command_options
  • python .\systrace.py -b 32768 -t 5 -a com.optimize.performance -o performance.html sched gfx view wm am app
  • 先执行命令,然后在操作app,就会生成一个html
  • 复制html链接在浏览器打开Android启动优化_第4张图片
  • 打开结果
    Android启动优化_第5张图片
  • cputime是代码消耗cpu的时间,耗性能的地方
  • walltime是代码执行时间

代码执行时间和cpu消耗时间不一样的情况,有可能在等待锁
选中方法按 M键查看详情

  • 你可能会遇到的问题
    • py版本不对,去下载2.7.12 和pywin
    • 缺少module six 库
  • systrace工具使用
    • https://www.jianshu.com/p/19b3245207e8
    • https://www.jianshu.com/p/fa6cfad8ccc2
    • https://www.cnblogs.com/1996swg/archive/2018/11/23/10007602.html
  • SystemClock.currentThreadTimeMillis(); cpu的执行时间

AspectJ工具使用

  • classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspecthx:2.0.0’
  • implementation ‘org.aspectj:aspectjrt:1.8.+’
  • apply plugin: ‘android-aspectjx’

哪些地方可以hook

  • 函数调用执行
  • get set 变量
  • 构造

Join Point (加入切入点)

  • 条件 ,符合条件的才切入

Advice

  • 一种Hook ,要插入代码的位置
    • Before : PointCut之前执行
    • After : PointCut之后执行
    • Around : PointCut之前.之后插入代码

语法介绍

@Before("execution(* android.app.Activity.on**(..))")
public void onActivityCalled(JoinPoint joinPoint) throw Throwable{  }
  • Before : Advice ,具体插入位置
  • execution : 处理Join Point 的类型,call execution
  • (* adnroid.app.Activity.on**(…)) :匹配规则 ,表示这个包名下的类以 on开头的方法都要切入代码
    • 第一颗 * 表示任意返回值类型
  • onActivityCalled :要插入的代码
@Aspect
public class PerformanceAop {

// 监控PerformanceApp类所有方法的执行时间
//@Around 方法前后都切入
    @Around("call(* com.optimize.performance.PerformanceApp.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.toShortString();
        long time = System.currentTimeMillis();
        try {
            joinPoint.proceed();//处理完
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
    }
    }

优化技巧

Theme切换

Android启动优化_第6张图片
Android启动优化_第7张图片

异步优化

  • 子线程分担主线程任务,并行减少时间
  • 问题
    • 第三方需要在主线程优化
    • 页面使用时候需要保证初始化完成
      • CountDownLatch mCountDownLatch= new CountDownLatch(1); //计数
      • mCountDownLatch.await();//等待中
      • mCountDownLatch.countDown();//这句代码没执行就会一直等待
    • B任务初始化需要A任务先初始化完成
  • 线程池异步优化

启动器

  • 充分利用CPU多核 ,自动梳理任务顺序
  • 代码Task化 ,启动逻辑抽象为Task
  • 根据任务依赖关系自动生成排序Task
  • 排序后多线程按优先级顺序执行
    Android启动优化_第8张图片
    启动器使用

延迟初始化

  • IdleHandler空闲的时候会触发回调 ,在优先级别很低的任务可以放在延迟优化里面
public class DelayInitDispatcher {

    private Queue mDelayTasks = new LinkedList<>();

    private MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            if(mDelayTasks.size()>0){
                Task task = mDelayTasks.poll();
                new DispatchRunnable(task).run();
            }
            //返回true就移除IdleHandler ,如果任务为空就移除
            return !mDelayTasks.isEmpty();
        }
    };

    public DelayInitDispatcher addTask(Task task){
        mDelayTasks.add(task);
        return this;
    }

    public void start(){
        Looper.myQueue().addIdleHandler(mIdleHandler);
    }

}

优化总方针

  • 异步,延迟,懒加载

其它方案

  • SharePerferences 提前加载 ,在mulitidex之前加载 ,系统代码提前加载没影响
  • 启动界面不启动服务,其它四大组件 ,比较耗时
  • 类加载优化,提前异步类加载
    • Class.forName() 只加载类本身及静态变量引用的类
    • new 类实例 可以额外加载类成员变量的引用类
  • 启动阶段抑制GC
  • CPU锁屏

你可能感兴趣的:(Android)