Android优化————启动优化

363 阅读4分钟

app启动过程

  1. 点击图标启动
  2. LauncherApp通知AMS进行启动,LauncherActivity onPause
  3. AMS新建app进程,创建ActivityThread,创建ApplicationThread
  4. 通过ApplicationThread向AMS注册Binder
  5. 执行Application的onCreate方法
  6. 新建进入的Activity
  7. 执行Activity的onCreate方法,进行UI绘制等操作

启动分类

  • 冷启动:从点击应用图标到UI界面完全显示且用户可操作的全部过程。

Click Event -> IPC -> Process.start -> ActivityThread -> bindApplication -> LifeCycle -> ViewRootImpl

  • 热启动:直接从后台切换到前台。

优化方向

可优化Application、Activity的创建以及回调过程

  1. 提前展示一个window(欢迎页),给用户友好的提示
  2. 避免启动做繁重密集的初始化操作
  3. 过度绘制,网络,io等优化

优化检测

  1. adb命令检测
// 其中的AppstartActivity全路径可以省略前面的packageName
adb shell am start -W [packageName]/[AppstartActivity全路径]
  • ThisTime:最后一个Activity的启动耗时
  • TotalTime:所有Activity的启动耗时
  • WaitTime:表示AMS启动Activity的总耗时。

一般读取WaitTime,为Application和Activity的初始化过程耗时。(冷启动耗时)

优缺点:

  • 线下使用方便,不能带到线上
  • 非精确时间
  1. 自定义打点查看耗时
  • 应用生命周期节点
  • 启动的初始化方法节点
  • 其他耗时业务,算法节点

优缺点:

  • 精确,可上线
  • 修改成本高

3.AOP打点 加入aspectjx库,打印出Application,Activity的耗时时间,进行针对优化

根路径build.gradle中添加

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

app中build.gradle中添加

apply plugin: 'android-aspectjx'
...
implementation 'org.aspectj:aspectjrt:1.8.+'

使用如下:

@Aspect
public class LauncherAop {

    @Around("call(* com.jw.myapplication.MainActivity.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) throws Exception{
        Signature signature = joinPoint.getSignature();
        String name = signature.toShortString();
        long time = System.currentTimeMillis();
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        Log.d("kangkang = ","kangaop = " +name+ " cost " + (System.currentTimeMillis() - time));
    }
}

4.TraceView

代码中开启

Debug.startMethodTracing();
method();
Debug.stopMethodTracing();

生成.trace文件,导入Android Studio,使用profile中的cpu查看文件生成的火炬图

优化方案

  1. 主题切换

设置自定义主题设置背景图,执行到onCreate方法后替换为Activiy的真实布局 2. 初始化分化 image

  • MultiDex以及Tinker的初始化操作
  • Application中的第三方组件的初始化
    1. 异步初始化组件,不阻塞主线程,设置异步线程为THREAD_PRIORITY_BACKGROUND
    2. 延迟初始化操作,再线程空闲时加载,
    3. EventBus、ota、bugly、migu、Linphone、Butterknife、地图、IOT
  • 设置线程池初始化任务
    1. 仿照AsyncTask新建线程池,核心线程数为2-4个
    2. 任务使用该线程池加载,如有顺序,使用CountDownLatch进行处理
  • 部分任务可以延迟加载,使用IdleHandler,在主线程空闲时加载
  • Multidex预加载优化

优化方案

在使用Aspect进行时间的监测时,发现Application和Activity中的初始化三方进程耗费了大量时间,在初始化时,我们开启了百度OTA服务,Bugly监测服务,咪咕音乐服务,阿里IOT服务,日志监测服务,Linphone语音服务,Ifly语音服务等,这些串行起来是比较耗时的。
所以我们采用开启一个线程池的方案,在子线程启动这些服务,对于OTA,IOT,日志检测,Linphone等服务不需要在第一时间初始化,所以放到线程池中根据执行顺序分别初始化。但是对于咪咕、Ifly和bugly来说,需要第一时间初始化,才能进行后边的逻辑,所以我们将这些服务优先初始化,并联合CountDownLatch,当必须的服务初始化完成后,才进入下面的流程。
对于必须要在主线程进行初始化的操作,可能会造成主线程繁忙卡顿,所以使用IdleHandler方法,在主线程空闲时执行,

具体优化了40%,由2.3s压缩到1.4s。

如果由任务A,B,C,D,要求C在A之后执行,D在B之后执行,那么直接将A,C合并为一个任务,放入线程池中运行,B、D合并为一个任务,放入线池程中执行,如需决定AC和BD的顺序,那么可以按照AC、BD的顺序依次放入子线程中。

项目地址:github.com/hanmingkang…

如何对IDLEHandler进行顺序划分?比如先执行B,在执行A

规划一个空闲队列,在Handler空闲时进行处理,每次出队优先级最高的,其他等到下次空闲在执行

2-4是怎么计算的?

核心线程数位2-4,计算方式是cpu核数-1,如果比2小,就选择2,比4大就选择4,中间就选它自己, 之所以 减掉这个1,是因为为了避免后台任务将 CPU 资源完全耗尽, 减掉的这个1 是留给我们 主线程 使用的。