Android 性能优化-启动优化


启动优化的第一步就是要获取启动的时间,整个启动逻辑的所需要的精确时长。根据各个方法的时长去定位哪些逻辑需要优化。首先我们如何获取代码的执行时间。如下

1.获取启动时间

1.1 启动分类

  • 冷启动:系统里没有对应的进程,需要新建进程和分配内存来启动程序。
  • 温启动:系统里已经存在对应的进程,进程在后台运行。但是界面的对象(activity)已经被杀死
  • 热启动:系统里已经存在对应的进程,进程还在后台运行,与用户可交互的界面控件和数据都还在。

1.2 启动时间的测量

1.2.1 adb 命令

查看启动时间。适合线下模式
注意:使用命令前,adb需要配置环境变量

adb shell am start -W com.example.demo/com.example.demo.MainActivity

This time:最后一个activity启动耗时
Total time;所有Activity启动耗时,因为有时候程序启动涉及好几个界面。
WaitTime:AMS启动Actvity总耗时。

1.2.2 手动记录时间

手动打点: 手动记录启动时间和结束时间,线上可以使用。
启动时间:attchBaseContext()
结束时间:不要记录首帧时间(windowsFocusChanged)。要记录第一个数据展示的时间
可以线上跟踪

1.2.3 AOP 面向切面编程

全称:Aspect Oriented Programming,适用于批量监控,统计等使用。
简单使用方式:
1.依赖
project里的build.gradle文件添加

  classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'

module里的build.gradle文件添加

apply plugin: 'android-aspectjx'

implementation 'org.aspectj:aspectjrt:1.8.+'

2.创建监听类

/**
 * 添加Aspect注解,且创建方法,来去监听Activity每个方法执行的时间
 * 方法名添加注解:
 *  @Around 在被监听的方法前后都执行逻辑。
 *  @before  在被监听的方法执行前执行
 *  @After 在被监听的方法后执行
 * 语法解释:call 调用
 * (* com..)* 返回值
 * com..具体的类
 * init** init开头的方法
 * (..)任意参数
 *
 */
@Aspect
public class AopDemo {

    private static final String TAG = "AopDemo";
    @Around("call (* com.example.aspectjdemo.MainActivity.init*(..))")
    public void getTime(ProceedingJoinPoint joinPoint){

        Signature signature = joinPoint.getSignature();
        String s = signature.toShortString();

        long start= System.currentTimeMillis();
        try {
            Log.d(TAG, "getTime: yy");
            //proceed调用 类似于执行监听方法,eg:initUmeng()
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        Log.d(TAG, s+"  getTime: "+(System.currentTimeMillis()-start));

    }
}

3.被监听的类和方法

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "AopDemo";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUmeng();

        initDialog();
    }

    private void initDialog() {
        Log.d(TAG, "initDialog: ");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void initUmeng() {
        Log.d(TAG, "initUmeng: ");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
无侵入 修改方便

1.3启动优化工具

1.3.1 traceView

图形展示执行时间,线程具体任务。适合线下检测状态。
好处:定点埋点,高效快捷。
通过执行代码,使用文件的形式收集代码执行逻辑的信息。
生成文件在SD卡:Android/data/packagename/files/xxx.trace

Debug.startMethodTracing("文件名")
Debug.stopMethodTracing()

图片:
wall time 代码执行时间
thread time cpu执行时间

缺点:运行时开销严重,整体会变慢。

1.3.2 systrace

获取内核数据,生成html报告。需要执行sdk里systrace命令。
api 18 以上 推荐TraceCompat

执行命令:

python /users/.../sdk/platform-tools/systrace/systrace.py -b 3200 -t 5 -a com.examle.demo -o hello.html sched gfx view wm am app
  • -b 文件大小
  • -t 时间
  • -a 包名
  • -o 输出文件名字
 TraceCompat.beginSection("app");
 TraceCompat.endSection();

轻量级,开销小,反映CPU利用率

cputime和walltime 区别
walltime代码执行时间
cputime代码消耗CPU时间

2.启动优化

异步优化就是去分担启动页面的其他任务,提高启动页的速度。

2.1 使用线程池加载各个子任务

把程序初始化的加载工作放到子线程,是一种直接高效的形式。我们可以创建线程池服务对象,执行N个线程调用每个初始化任务即可;当然这些初始化的任务也有特殊的情况存在。如:需要上下文环境Context,需要主线程的Looper对象,又或者此任务需要在主线程运行。也就需要特殊处理,如传入context,Looper,Handler对象。
缺点:代码优雅度不好,阅读性差。

2.2 子任务之间有依赖关系

有一种情况更为特殊,就是子任务之间存在依赖关系,这时候需要我们用到CountDownLatch。定时器概念,初始化计数值,当减到为0时,就可以执行下一个任务。

CountDownLatch count= new CountDownLatch(1);
//执行减一
count.countDown();

//如果为count==0,则开始执行下面逻辑
count.await();

2.3 启动器

自定义一套启动器,类似于线程池的原理,同时添加了任务之间的依赖关系处理,可以实现主线程执行和子线程执行,是否等待其他任务。
优点:充分利用CPU的多核,自动梳理执行流程
常规异步的缺点:代码不优雅,不好处理,如子任务有依赖关系。

1.抽取为task任务
2.所有关系生成一个有向无环图。
3.多线程的优先级依次执行
简陋原理图
代码资源:
https://download.csdn.net/download/chentaishan/20033656

2.4 延迟方案idleHandler

对于一些不紧急的任务,我我们可以延迟加载。这里使用idleHandler
源码解析:
https://blog.csdn.net/chentaishan/article/details/104437160

2.5 其他

提前加载SharedPrefrences
multidex之前加载,利用此阶段CPU

启动阶段不启动子进程
子进程会共享CPU资源,导致主进程CPU紧张
启动顺序:app onCreate 之前是contentprovider

类加载优化:提前异步类加载
class.forName() 只加载类本身及静态变量的引用类
new 可以额外加载类成员变量的引用类

启动阶段抑制GC
cpu 锁频 CPU的线程全部打开

总结

如何进行启动优化的?
从过程进入主题,分析现状,确认问题。
针对性优化,针对问题 怎么优化问题
长期保持优化效果。 ci监控 启动器
异步问题?演进过程
详细介绍启动器
容易忽略的注意点;
cpu time wall time
注意延迟初始化优化
类加载
CPU拉高

版本迭代导致启动变慢的解决方式
结合CI
完善监控

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值