【性能优化】Android冷启动优化

文章目录


常见现象

各种第三方工具初始化和大量业务逻辑初始化,影响启动时间,导致应用启动延迟、卡顿等现象

APP的启动流程

加载和启动应用程序;

App启动之后立即展示出一个空白的启动窗口;

创建App程序的进程;

创建App对象;

启动Main Thread;

创建启动页的Activity;

加载View;

布置屏幕;

进行初始绘制;

详细启动流程可以看这篇文章
APP启动流程

计算启动时间

看一张官图

上图是Google提供的冷启动流程图,可以看到冷启动的起始点时Application.onCreate()方法,结束点在ActivityRecord.reportLanuchTimeLocked()方法。

我们可以通过以下两种方式查看冷启动的耗时

Displayed Time

在Android 4.4(API级别19)及更高版本中,logcat包含一个名为Displayed的log信息,此值表示启动过程和完成在屏幕上绘制相应活动之间所经过的时间量。

java 复制代码
	adb logcat | grep Displayed
	05-08 19:18:14.303   419   477 I ActivityTaskManager: Displayed ***/.ui.HomeActivity: +1s382ms
	

adb dump

java 复制代码
	adb shell am start -S -W ***/.ui.HomeActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN
	
	Stopping: ***
	Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=***/.ui.HomeActivity }
	Status: ok
	LaunchState: COLD
	Activity: ***/.ui.HomeActivity
	TotalTime: 1391
	WaitTime: 1392
	Complete

ThisTime:是指调用过程中最后一个Activity启动时间到这个Activity的 startActivityAndWait调用结束;

TotalTime:是指调用过程中第一个Activity的启动时间到最后一个Activity的 startActivityAndWait结束。

WaitTime:是startActivityAndWait这个方法的调用耗时;

此方法通过终端ADB命令查看启动指定页面(应用首页)的耗时,即应用冷启动的时间。

启动优化具体策略

  1. 启动白屏或黑屏问题
    设置启动页
java 复制代码
<style name="WelcomeTheme" parent="Theme.AppCompat.Light.NoActionBar.FullScreen">
    <item name="android:windowBackground">@drawable/shape_welcome</item>
    <item name="android:windowDrawsSystemBarBackgrounds">false</item>
</style>
  1. Application启动优化
    attachBaseContext()的优化

    主要是MultiDex启动优化的,但是MultiDex启动优化存在一定风险,主dex经过一系列的优化操作减少了主dex的大小,因此也增大了NoClassDefFoundError的异常的可能,此时会导致我们的应用启动失败的风险。所以MultiDex启动优化一定要做好检查测试工作。

    onCreate()

    Application的onCreate()随着业务的复杂会存在大量的第三方库的初始化和组件初始化,导致该生命周期过于沉重,影响启动时间。对onCreate()的优化可以分为四类

    1、必须在onCreate()且是主进程中初始化

    2、可以延迟,但是需要在Application中初始化

    3、可以延迟到启动页的生命周期回调中初始化

    4、延迟到用的时候再初始化

    此处的优化着力会比较多,牵涉的代码业务逻辑比较复杂,核心是减轻冷启动初始化的工作,延迟初始化和按需初始化。

  2. 寻找有效的结束回调

    1、IdleHandler

    从冷启动流程图看,结束时间是在UI渲染完计算的,所以很明显,Activity生命周期中的onCreate()、onResume()、onStart()都不能作为冷启动的结束回调。常规操作中用Handler.postDelay()问题在于Delay的时间不固定,但我们知道消息处理机制中,MessageQueue有个ArrayList。可以在列表中添加Idle任务,Idle任务列表只有MessageQueue队列为空时才会执行,也就是所在线程任务已经执行完时,线程处于空闲状态时才会执行Idle列表中的任务。

java 复制代码
public class MainActivity extends Activity {

    private static final Handler sHandler = new Handler(Looper.getMainLooper());
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override    
        public boolean queueIdle() {   
            // 页面启动所需耗时初始化            
            doSomething();
            return false;
        }});
    }
}

2、onWindowFocusChanged()

在冷启动结束即UI渲染完在去处理初始化操作,也是达到延迟初始化的目的。

java 复制代码
@Override
    public void onWindowFocusChanged(boolean hasFocus) {    
        super.onWindowFocusChanged(hasFocus);    
        if (onCreateFlag && hasFocus) {
            onCreateFlag = false;
            
            sHandler.post(new Runnable() {
                @Override
                public void run() {
                    doSomething();
                }
            })
        }
    }

3、通过View.post(Runnable runnable)方法实现

View内部维护了一个HandlerActionQueue,我们可以在DecorView attachToWindow前,通过View.post()将任务Runnables存放到HandlerActionQueue中。当DecorView attachToWindow时会先遍历先前存放在HandlerActionQueue的任务数组,通过handler挨个执行。

java 复制代码
  // 方案只有在onResume()或之前调用有效
    protected void postAfterFullDrawn(final Runnable runnable) {    
        if (runnable == null) {        
            return;    
        }    
        getWindow().getDecorView().post(new Runnable() {        
            @Override        
            public void run() {            
                sHandler.post(runnable);
            }    
        });
    }

总结

冷启动优化主要是两大方面一是启动页的设置;而是初始化的管理包含延迟初始化和按需初始化,根本还是减少不必要的初始化逻辑,尽量轻量化。

参考链接

  1. 【性能优化】Android冷启动优化
  2. Android应用优化之冷启动优化
相关推荐
大白要努力!25 分钟前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee32 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood43 分钟前
Perfetto学习大全
android·性能优化·perfetto
EterNity_TiMe_1 小时前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
程序猿进阶2 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
Dnelic-4 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen6 小时前
MTK Android12 user版本MtkLogger
android·framework
工业甲酰苯胺10 小时前
Redis性能优化的18招
数据库·redis·性能优化
长亭外的少年13 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
无尽的大道16 小时前
深入理解 Java 阻塞队列:使用场景、原理与性能优化
java·开发语言·性能优化