前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android性能优化(一)—— 启动优化,冷启动,热启动,温启动

Android性能优化(一)—— 启动优化,冷启动,热启动,温启动

作者头像
233333
发布2024-06-28 09:10:20
2020
发布2024-06-28 09:10:20
举报
文章被收录于专栏:linux驱动个人学习

APP启动方式

App启动方式分三种:冷启动(cold start)、热启动(hot start)、温启动(warm start)

▲ 冷启动

系统不存在App进程(APP首次启动或APP被完全杀死)时启动APP此时,APP的启动将经历两个阶段:

第一阶段

1.加载并启动app;2.app启动后,第一时间为app显示一个空白的window;3.创建app进程

第二阶段

系统一旦创建了app进程,app进程就要负责做以下的任务:

1.创建app对象;2.启动主进程ActivityThread;3.创建MainActivity;4.渲染视图;5.执行onLayout;6.执行onDraw7.完成第一次绘制后,把mainActivity替换已经展示的BackgroundWindow,即空白window。

▲ 热启动

当我们按了Home键或其它情况app被切换到后台,再次启动app的过程。热启动时,系统将activity带回前台。如果应用程序的所有activity存在内存中,则应用程序可以避免重复对象初始化、渲染、绘制操作。 如果由于内存不足导致对象被回收,则需要在热启动时重建对象,此时与冷启动时将界面显示到手机屏幕上一样。

▲ 温启动

温启动包含了冷启动的一些操作,由于app进程依然在,温启动只执行冷启动的第二阶段,这代表着它比热启动有更多的开销。

温启动有很多场景,例如:

  • 用户按连续按返回退出了app,然后重新启动app;
  • 由于系统收回了app的内存,然后重新启动app

App启动优化

app启动优化的方向是冷启动。

空白window问题

app启动时,会短暂的一瞬间白屏,这个动图是我在Application的oncCreate里线程休眠1s实现。虽然如此,但是实际项目中确实存在启动时白屏时间过长的问题。

要看怎么解决,先问为什么会有白屏

在上面讲冷启动第一阶段,启动app之后,立即展示一个空白的window,那么具体怎么展示,为什么要展示空白的window呢?

我们看app启动流程加载首屏部分源码:

ActivityStack

代码语言:javascript
复制
     // Set to false to disable the preview that is shown while a new activity
    // is being started.
    private static final boolean SHOW_APP_STARTING_PREVIEW = true;

    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
       //…… 省略一万行……

        if (!isHomeOrRecentsStack() || numActivities() > 0) {
            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                    "Prepare open transition: starting " + r);
            //…… 省略一万行……

            boolean doShow = true;
            if (newTask) {
                // Even though this activity is starting fresh, we still need
                // to reset it to make sure we apply affinities to move any
                // existing activities from other tasks in to it.
                // If the caller has requested that the target task be
                // reset, then do so.
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                // tell WindowManager that r is visible even though it is at the back of the stack.
                r.setVisibility(true);
                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            // 如果同时满足,则显示空白屏幕
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                //……
                //显示空白屏幕
                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
            }
        } else {
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
            ActivityOptions.abort(options);
        }
    }

这里省略了部分代码,只保留关键代码。

显示白屏由两个变量决定:SHOW_APP_STARTING_PREVIEW,在activity启动前时否显示预览;doShow,其默认值为true,但是它由newTask决定,是否为一个全新的activity栈,也就是说,SHOW_APP_STARTING_PREVIEW为true,并且app冷启动,就显示白屏。

那么,能不能不显示白屏呢?

答案是可以的。

两种方法:

  • 禁用app启动时window预览的功能

在主题中为首屏activity添加一个注意,禁用window预览的功能,并在manifest中使用

代码语言:javascript
复制
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

    </style>

    <!--禁用预览功能主题-->
    <style name="AppSpalshNoPreviewTheme" parent="AppTheme">
        <item name="android:windowFullscreen">true</item>
        <item name="windowNoTitle">true</item>

        <!--        <item name="android:windowContentOverlay">@null</item>-->
        <!--        <item name="android:windowBackground">@mipmap/wall</item>-->
        <item name="android:windowDisablePreview">true</item>
    </style>
</resources>



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.margin.myapplication">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:name=".MyApplication"
    android:theme="@style/AppTheme"
    tools:ignore="GoogleAppIndexingWarning">


    <activity android:name=".SplashActivity"
        android:theme="@style/AppSpalshNoPreviewTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name=".HomeActivity"
        android:launchMode="singleTask"/>
</application>

</manifest>

在网上看到说可以将首屏背景设为透明背景,就是我注释掉的两行,但是实际测试发现会透明,但是会出现黑屏的问题,估计这种也不靠谱还麻烦,所以直接忽略它,直接禁用预览功能。

效果如下:

可以看到,我们点了一下app,系统竟然卡住了?!过了1秒多,app才启动起来??什么鬼?其实并不是系统卡住了,而是我们本来应该显示的空白预览屏幕不显示了,所以看起来好像卡住了。嘿嘿嘿,此处突然萌生了一个想法:你手机或者系统有问题,跟我的app代码有啥关系😏😏😏,哈哈哈,你品,你细品,哈哈哈哈……

言归正传,这种方式虽然可以帅锅给系统,并且之前支付宝好像竟然这么干过!但是并不是好的办法,接下来,我们往下看另一种方法。

  • 给空白首屏Activity设置一个背景

在style.xml中给SplashActivity添加一个新主题,设置一个背景:

代码语言:javascript
复制
    <!--替换首屏的背景的主题-->
    <style name="AppSpalshBackgroundTheme" parent="AppTheme">
        <item name="android:windowBackground">@mipmap/wall</item>
        <item name="android:windowFullscreen">true</item>
        <item name="windowNoTitle">true</item>
    </style>

效果如下:

看这种效果是不是很好,如果UI再改一下,UI妹子给做一些好看的图,是不是效果更棒了。(另外我发现头条好像也是这样的)

从源头解决

上面两种方式,看似挺不错,尤其是第二种,效果竟然看着还不错??😂😂😂

但是!上面的方式治标不治本只是障眼法,我们还是要从根源上解决问题!

从上面冷启动的介绍可知,app启动过程中,会有如下几个重要过程:

看出点什么了吗?app启动,显示白屏,首屏第一次绘制完成,替换白屏。也就是说,直到首屏显示之前,都是白屏!

所以,要想解决白屏的问题,就要减少白屏显示的时间!也就是说要加快app初始化和首屏绘制的时间。

像上面,我实现长时间白屏,就是在application的onCreate方法中,让线程休眠一段时间

代码语言:javascript
复制
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

看到这段代码,是不是让你产生了一些思路呢?

  • 减少Application 初始化的工作量。

我们通常会在Application的onCreate方法中可以做一些轻量化的初始化操作。当onCreate任务越多耗时操作越多,初始化越慢,于是splashActivity启动时间就越晚,白屏时间就越长咯。

一些三方的SDK,文件,设备,线程池,等等工具类,等等尽可能不要在application中初始化。

  • 加快UI绘制

其实这个已经是老生常谈的话题了,常常由一下几种方案:

  1. 减少布局嵌套层级。

布局层级越深,执行measure,layout次数就越多越耗时,ui绘制自然就越慢。我曾经见过一位大神,一个xml嵌套8-9层甚至十几层!我始终想不明白大神到底是计算机专业的还是护理专业的。

此刻我不得不说约束布局——ConstraintLayout是真的香!只要你用的好,布局合理,甚至可以零嵌套!

  1. 减少布局元素

布局中元素越多,单帧UI绘制就越耗时,所以尽可能减少布局元素。事实上,不管是首屏还是其他的activity或者fragment布局都应该尽可能的减少布局元素。注意有时候可能为了方便,布局中放了一些不可见或占位的控件,应该减少这种情况,因为他们也参与绘制过程的。另外还可以使用viewStub标签,使用时再初始化需要的布局,非常方便。

  1. 避免过度绘制减少不必要的背景设置。尽可能避免元素重叠。既设置了背景又重叠,就会有不必要的耗时绘制。还是要减少不必要的站位元素。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • APP启动方式
    • ▲ 冷启动
      • ▲ 热启动
        • ▲ 温启动
          • 空白window问题
      • App启动优化
        • 从源头解决
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档