Android 启动优化

启动窗口优化

启动应用后会显示空白启动窗口,可给这个窗口这是一张背景图代替白屏(通过theme设置)。

image.png

异步任务

image.png

1,初始化逻辑抽象成一个个Task

2,根据任务的依赖关系,使用 BFS(广度优先) 构建出有向无环图,并得到拓扑排序,获取任务的执行顺序。

1.1,首先找出所有入度为 0 的队列,用 queue 变量存储

1.2,当队列不为空,进行循环判断。

  • 从队列 pop 出,添加到结果队列

  • 遍历当前任务的子任务,通知他们的入度减一(其实是遍历 taskChildMap),如果入度为 0,添加到队列 queue 里面

1.3,当结果队列和 list size 不相等试,证明有环

3,在多线程执行过程中,通过任务的依赖和 CounDownLatch 确保先后执行关系的

假设a是b的一个前置任务,当一个任务b执行时:

2.1,如果CounDownLatch>0,说明前置任务a没有执行完毕,任务b等待;

2.2,前置任务a执行完毕,通知任务b,b任务的CounDownLatch减一;

2.3,任务b的CounDownLatch如果为0,执行b任务。

任务的CounDownLatch为前置任务的数量。初始化任务时会设置当前任务的前置任务列表,所以可以通过当前任务获取到前置任务列表及其数量。

真正的任务是DispatchRunnable,上述任务指DispatchRunnable,它持有Task并在其run方法中调用了Task的run方法。

await方法实现原理,也是通过将使用CounDownLatch记录NeedWait的任务数,NeedWait任务执行完就减1,CounDownLatch如果为0,才执行后续任务(拓扑图任务后面的任务)

注意:异步任务必须使用线程池,避免线程过多,cpu过度切换

延迟任务

public class DelayInitDispatcher {

    private Queue<Task> 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();
            }
            return !mDelayTasks.isEmpty();
        }
    };

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

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

复制代码

Handler().postDelayed()的痛点:

1,时机不容易控制:handler postDelayed指定的延迟时间不好估计。

2,导致界面UI卡顿:此时用户可能还在滑动列表。

使用IdleHandler,Handler 空闲的时候才会被调用,如果返回 true, 则会一直执行,如果返回 false,执行完一次后就会被移除消息队列。

当CPU空闲时,去执行延迟初始化的task,一个一个地拿出来并执行。这种分批执行的好处在于每一个task占用主线程的时间相对来说很短暂,并且此时CPU是空闲的,这样能更有效地避免UI卡顿。

需要注意的是,能异步的task(或者是必须在Application的onCreate方法完成前必须执行完的非异task务),优先使用异步启动器在Application的onCreate方法中加载,对于不能异步的task,我们可以利用延迟启动器进行加载。如果任务可以到用时再加载,可以使用懒加载的方式。

问题:用户使用某个库时,如果还没有初始化怎么处理?

类重排

通过 ReDex 的 Interdex 调整类在 Dex 中的排列顺序,把启动时需要加载的类按顺序放在主 dex 里。具体实现可以参考 Redex 初探与 Interdex:Andorid 冷启动优化。

抖音BoostMultiDex优化

针对5.0以下的系统:

具体的实现原理为:在第一次启动的时候,直接加载没有经过 OPT 优化的原始 DEX,先使得 APP 能够正常启动。然后在后台启动一个单独进程,慢慢地做完 DEX 的 OPT 工作,尽可能避免影响到前台 APP 的正常使用。绕过 ODEX 直接加载 DEX 的方案如下:

1)、从 APK 中解压获取原始 Secondary DEX 文件的字节码
2)、通过 dlsym 获取dvm_dalvik_system_DexFile数组
3)、在数组中查询得到Dalvik_dalvik_system_DexFile_openDexFile_bytearray函数
4)、调用该函数,逐个传入之前从 APK 获取的 DEX 字节码,完成 DEX 加载,得到合法的DexFile对象
5)、把DexFile对象都添加到 APP 的PathClassLoader的 pathList 里

主页面数据预加载

splash页面的时候使用异步任务提前将主页面的数据请求下来,主页面加载的时候可以直接从内存读取。

闪屏页与主页的绘制优化

1、分析布局,减少布局嵌套或者替换消耗性能少的布局(FrameLayout,constraintlayout)

2、使用include+merge减少布局层级

3、使用viewstub提供按需加载

4、复用系统自带的资源 winow background

内存优化避免gc占用cpu时间

启动过程中减少 GC 的次数

1,避免进行大量的字符串操作,特别是序列化和反序列化

2,频繁创建的对象需要考虑复用

3,转移到 Native 实现

减少冷启动的次数

MainActivity 返回用隐藏代替销毁

 override fun onBackPressed() {
        //super.onBackPressed()
        moveTaskToBack(true)
    } 
复制代码

webview优化

如果主页面打开后有webview显示广告。

1,webview提前初始化及复用

2,使用离线包

3,使用数据预加载

参考

深入探索Android启动速度优化
Android 优化之 App 启动优化

Android 启动优化(四)- AnchorTask 是怎么实现的

www.androidperformance.com/2019/11/18/…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享