android性能优化之启动优化(代码片段)

bubbleben bubbleben     2023-01-07     214

关键词:

Android性能优化之启动优化

1. 启动窗口优化

Android系统在Activity的窗口尚未启动完成前,会先显示一个启动窗口(Starting Window),等界面的第一帧渲染完成后再从启动窗口切换到真正的界面显示,启动窗口通常情况下为纯黑或者纯白的背景,如果应用启动慢则启动窗口的显示时间就会变长,给用户的感官就是白屏或者黑屏时间长即启动慢,通常会通过windowDisablePreview这个属性禁用系统默认启动窗口,然后为Activity提供自定义的主题背景(尽量使主题背景与和主页一致),这么做的目的主要是为了消除启动时的黑白屏,并不会真正减少用户启动时间,而且可能导致启动时间比不禁用启动窗口的应用更长(启动自己的窗口需要的时间可能要比直接显示系统的启动窗口所花的时间要长)。

[-> ActivityRecord.java]

boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
        CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
        IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
        boolean allowTaskSnapshot, boolean activityCreated) 
        ...
        // If this is a translucent window, then don't show a starting window -- the current
        // effect (a full-screen opaque starting window that fades away to the real contents
        // when it is ready) does not work for this.
        ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Checking theme of starting window: 0x%x", theme);
        if (theme != 0) 
            AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
                    com.android.internal.R.styleable.Window,
                    mWmService.mCurrentUserId);
            if (ent == null) 
                // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
                // see that.
                return false;
            
            final boolean windowIsTranslucent = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
            final boolean windowIsFloating = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsFloating, false);
            final boolean windowShowWallpaper = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowShowWallpaper, false);
            final boolean windowDisableStarting = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowDisablePreview, false);
            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Translucent=%s Floating=%s ShowWallpaper=%s",
                    windowIsTranslucent, windowIsFloating, windowShowWallpaper);
            if (windowIsTranslucent) 
                return false;
            
            if (windowIsFloating || windowDisableStarting) 
                return false;
            

从代码中我们可以到,除了windowDisablePreview以外,我们还可以通过配置windowIsFloating或者windowIsTranslucent属性来禁用启动窗口;

2. 布局优化

2.1 减少冗余或嵌套布局

2.2 使用 ViewStub 替代在启动过程中不需要显示的 UI 控件

2.3 使用merge标签减少布局的嵌套层次

3. 线程优化

Java的线程是抢占式的,任一时刻只有一个线程占用CPU并处于运行状态;多线程并发的条件下,虚拟机按照一定的规则进行线程调度并分配CPU使用权;线程优化主要是减少 CPU 调度带来的波动,让启动时间更稳定。如果启动过程中有太多线程,会导致CPU频繁切换反而降低运行效率,尤其是比较低端的机器。同时运行过多的线程会让主线程的 SleepRunnable 状态变多,降低了应用的启动速度;

3.1 异步初始化

Android的任务是以消息的方式驱动的,包括四大组件生命周期在内的所有事件都是在主线程的消息队列中排队执行,如果主线程执行了耗时任务,将会阻塞启动过程导致启动时间增长甚至可能触发ANR,所以需要将IO 、网络等耗时任务放在子线程处理。

异步初始化应当尽量避免通过创建Thread来实现,因为通过Thread创建的线程无法复用,而且频繁创建和销毁会带来更大的开销,而且如果在创建线程时未通过Thread.setName设置线程名称,则无法准确定位线程归属;建议使用Android自带的HandlerThreadIntentServiceAsyncTask等工具也可以用来实现异步;

3.2 延时初始化

除了将耗时任务放在异步线程初始化以外,还可以通过延时初始化的方式将不重要的工作推后进行;通常情况下我们可以通过Handler.postDelayed将任务延时执行,但是缺点比较明显,那就是延时时机不容易把握,如果延时太短可能会影响主线程消息的执行,太长则可能错过用户的交互和界面的展示;

推荐的做法是通过IdleHandler的特性,在消息队列空闲时执行任务:当 MessageQueue 空闲时任务才会被调用,如果返回 true则会一直执行,否则执行完一次后就会从消息队列中移除。

3.3 通过线程池管理线程

通过线程池控制线程的数量:线程数量太多会相互竞争 CPU 资源,从而使 CPU 上下文切换的次数变多,导致分给主线程的时间片减少,从而导致启动速度变慢;线程池除了能够复用线程减少频繁创建和销毁带来的开销以外,还能够提供如定时和任务队列等更强大的并发功能;

3.4 设置子线程优先级

根据任务具体情况来评估优先级,通过Process.setThreadPriority()设置线程优先级:优先级类别定义在Process.java文件中,可以根据实际需要来设置,建议非紧急任务设置为 THREAD_GROUP_BACKGROUND,这样即能保证工作线程运行,也不会阻塞主线程;

[ -> Process.java]

/**
 * Default thread group -
 * has meaning with setProcessGroup() only, cannot be used with setThreadGroup().
 * When used with setProcessGroup(), the group of each thread in the process
 * is conditionally changed based on that thread's current priority, as follows:
 * threads with priority numerically less than THREAD_PRIORITY_BACKGROUND
 * are moved to foreground thread group.  All other threads are left unchanged.
 * @hide
 */
public static final int THREAD_GROUP_DEFAULT = -1;

/**
 * Background thread group - All threads in
 * this group are scheduled with a reduced share of the CPU.
 * Value is same as constant SP_BACKGROUND of enum SchedPolicy.
 * @hide
 */
public static final int THREAD_GROUP_BACKGROUND = 0;

/**
 * Foreground thread group - All threads in
 * this group are scheduled with a normal share of the CPU.
 * Value is same as constant SP_FOREGROUND of enum SchedPolicy.
 * Not used at this level.
 * @hide
 **/
private static final int THREAD_GROUP_FOREGROUND = 1;

/**
 * System thread group.
 * @hide
 **/
public static final int THREAD_GROUP_SYSTEM = 2;

/**
 * Application audio thread group.
 * @hide
 **/
public static final int THREAD_GROUP_AUDIO_APP = 3;

/**
 * System audio thread group.
 * @hide
 **/
public static final int THREAD_GROUP_AUDIO_SYS = 4;

/**
 * Thread group for top foreground app.
 * @hide
 **/
public static final int THREAD_GROUP_TOP_APP = 5;

/**
 * Thread group for RT app.
 * @hide
 **/
public static final int THREAD_GROUP_RT_APP = 6;

/**
 * Thread group for bound foreground services that should
 * have additional CPU restrictions during screen off
 * @hide
 **/
public static final int THREAD_GROUP_RESTRICTED = 7;

4. GC优化

在启动过程中要尽量减少 GC 的次数,避免造成主线程长时间的卡顿;启动过程中要避免创建大量的对象,特别是序列化跟反序列化过程:要避免进行大量的字符串操作,特别是序列化和反序列化;避免对象的频繁创建,需要考虑对象复用;考虑将逻辑转移到 Native 实现;Java 对象的逃逸也很容易引起 GC 问题,应该保证对象生命周期尽量的短,在栈上就进行销毁;

5. IO优化

启动过程中系统负载比较高,有许多系统 IO 都在此时发生,这时候 IO 的性能下降会比较快,此时应用的 IO 操作会比平时更慢一些,尤其是在性能比较差的机器上;

IO 分网络 IO 和磁盘 IO ,启动过程中不建议进行网络 IO ,而对于磁盘 IO 则需要细分:

  1. 需要清楚启动过程中读了什么文件、多少个字节、 Buffer 是多大,使用了多长时间、在什么线程等一系列信息;
  2. 进行启动过程中的 IO 监控,微信在监控 IO 时发现有用户的 db 文件达到了 500MB

6. 厂商优化

6.1 微信

mmkernel

微信Android热补丁实践演进之路:https://mp.weixin.qq.com/s/-NmkSwZu83HAmzKPawdTqQ

6.2 阿里

Alpha

6.3 Facebook

Redex:https://github.com/facebook/redex

6.4 抖音

抖音BoostMultiDex优化实践:https://juejin.cn/post/6844904079206907911

6.5 支付宝

支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能:https://mp.weixin.qq.com/s/79tAFx6zi3JRG-ewoapIVQ

支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」:https://juejin.cn/post/6844903705028853767

6.6 爱奇艺

爱奇艺Android客户端启动优化与分析:https://mp.weixin.qq.com/s/eaArt5Udc4WZ3NoH5RlEkQ

7. 系统优化

7.1 PreFork

Android Q 加入了 PreFork 机制,会先 fork 几个空进程,当应用启动的时候,可以直接复用这几个空进程,而不用重新fork

7.2 启动加速

应用启动时系统会对其做绝对的资源倾斜,比如 CPUIOGPU 等,这一点大家抓个 Systrace 看一下即可,不管是频率还是调度算法,正在启动的 App 绝对是当时的系统 VIP 客户;

7.3 主线程/渲染线程加速

部分厂家会对应用的主线程和渲染线程做特殊对待,比如让他们直接跑到大核上,将其他不重要的线程移到小核;

7.4 iowrap

7.2 启动加速

应用启动时系统会对其做绝对的资源倾斜,比如 CPUIOGPU 等,这一点大家抓个 Systrace 看一下即可,不管是频率还是调度算法,正在启动的 App 绝对是当时的系统 VIP 客户;

7.3 主线程/渲染线程加速

部分厂家会对应用的主线程和渲染线程做特殊对待,比如让他们直接跑到大核上,将其他不重要的线程移到小核;

7.4 iowrap

android面试之必问性能优化(代码片段)

对于Android开发者来说,懂得基本的应用开发技能往往是不够,因为不管是工作还是面试,都需要开发者懂得大量的性能优化,这对提升应用的体验是非常重要的。对于Android开发来说,性能优化主要围绕如下方面展开:启动优化... 查看详情

android性能优化之启动耗时测量(代码片段)

Android启动优化之启动耗时测量本文基于Android11.0源码分析,涉及如下文件frameworks/base/services/core/java/com/android/server/wm/ActivityMetricsLogger.javaframeworks/base/services/core/java/com/android/server/wm/ActivityR 查看详情

android性能优化之启动耗时测量(代码片段)

Android启动优化之启动耗时测量本文基于Android11.0源码分析,涉及如下文件frameworks/base/services/core/java/com/android/server/wm/ActivityMetricsLogger.javaframeworks/base/services/core/java/com/android/server/wm/ActivityR 查看详情

❤️android性能优化之启动优化❤️(代码片段)

...赞、收藏、评论粉丝福利:公众号「帅次」一个分享Android体系技术·相关知识·面试题库·技术互助·干货·资讯·高薪职位·教程的地方。🔥背景        用户希望应用能够快速打开。启动时间过长的应用不能满足这个... 查看详情

android面试之必问性能优化(代码片段)

对于Android开发者来说,懂得基本的应用开发技能往往是不够,因为不管是工作还是面试,都需要开发者懂得大量的性能优化,这对提升应用的体验是非常重要的。对于Android开发来说,性能优化主要围绕如下方... 查看详情

android性能优化之内存泄漏检测以及内存优化(下)(代码片段)

上篇博客我们写到了Android中内存泄漏的检测以及相关案例,这篇我们继续来分析一下Android内存优化的相关内容。  上篇:Android性能优化之内存泄漏检测以及内存优化(上)。  中篇:Android性能优化之内... 查看详情

android性能优化之内存泄漏检测以及内存优化(中)(代码片段)

上篇博客我们写到了Java/Android内存的分配以及相关GC的详细分析,这篇博客我们会继续分析Android中内存泄漏的检测以及相关案例,和Android的内存优化相关内容。  上篇:Android性能优化之内存泄漏检测以及内存优化&... 查看详情

android性能优化之启动速度优化(代码片段)

前言:文本主要会介绍三大块:1.简略介绍APP启动的完整流程,对整个流程有所了解,才知道在哪里可以进行优化。2.一些常用的APP启动优化的方案,主要分为三大块优化方向。3.一些不常见的APP启动优化的方... 查看详情

android性能优化-启动速度优化(代码片段)

文章目录1.启动的状态2.冷启动耗时2.1系统日志统计2.2adb命令统计3.启动分析3.1CPUProfile工具简单教程3.2启动耗时分析3.3使用DebugApi生成.trace文件4.StrictMode严苛模式5.结尾做开发除了实现功能,还要注重优化,性能优化包括的... 查看详情

android性能优化之绘制优化(代码片段)

前言前一段时间,笔者带大家一起深入探索Android布局优化和深入探索Android卡顿优化,内容难度比较大,因此,本篇文章就是上述两篇文章的基础篇,掌握这篇文章的知识后,阅读上面两篇文章的难度会小... 查看详情

android性能优化实践与总结(包含启动,内存优化)(代码片段)

应用中性能优化实践与总结(精心总结)任何优化都需要进行检测,以数据说话,优化前和优化后有了怎样的提升[TOC]启动优化检测启动时间检测工具任选其一hugo插件,自己定义时间开始和结束手动计算时间.AOP工具AspectJadb的amstart命令... 查看详情

抖音android性能优化系列:启动优化实践(代码片段)

组件,其初始化就是借助了一个叫ProcessLifecycleOwnerInitializer的ContentProvider进行初始化的。LifeCycle的初始化只是进行了Activity的LifecycleCallbacks的注册耗时不多,我们在逻辑层面上不需要做太多的优化。值得注意的是,如果这类用于... 查看详情

性能优化之java(android)代码优化

性能优化之Java(Android)代码优化本文为Android性能优化的第三篇——Java(Android)代码优化。主要介绍Java代码中性能优化方式及网络优化,包括缓存、异步、延迟、数据存储、算法、JNI、逻辑等优化方式。(时间仓促,后面还会... 查看详情

android性能优化之疑难杂症解决方案,u-apm的性能监控分析(代码片段)

关于Android发展至今,在各项功能十分成熟的情况下,我们越来越重视App的性能优化,以及用户体验,这关乎一个线上应用的DAU持续增长的基础,以及用户口碑的问题,今天刘某人带大家来一起分析一下崩... 查看详情

android进阶——性能优化之bitmap位图内存管理及优化概述(代码片段)

文章大纲引言一、Bitmap概述二、Bitmap家族的重要成员对象1、Bitmap2、Bitmap.Config3、Bitmap.CompressFormat4、BitmapFactory5、BitmapFactory.Options6、BitmapRegionDecoder7、BitmapShader三、Bitmap的内存1、图片的像素与内存四、位图的底层存储位置五、Bitm... 查看详情

android进阶——性能优化之bitmap位图内存管理及优化概述(代码片段)

文章大纲引言一、Bitmap概述二、Bitmap家族的重要成员对象1、Bitmap2、Bitmap.Config3、Bitmap.CompressFormat4、BitmapFactory5、BitmapFactory.Options6、BitmapRegionDecoder7、BitmapShader三、Bitmap的内存1、图片的像素与内存四、位图的底层存储位置五、Bitm... 查看详情

android进阶——性能优化之bitmap位图内存管理及优化概述(代码片段)

文章大纲引言一、Bitmap概述二、Bitmap家族的重要成员对象1、Bitmap2、Bitmap.Config3、Bitmap.CompressFormat4、BitmapFactory5、BitmapFactory.Options6、BitmapRegionDecoder7、BitmapShader三、Bitmap的内存1、图片的像素与内存四、位图的底层存储位置五、Bitm... 查看详情

性能优化之java(android)代码优化

本文为Android性能优化的第三篇——Java(Android)代码优化。主要介绍Java代码中性能优化方式及网络优化,包括缓存、异步、延迟、数据存储、算法、JNI、逻辑等优化方式。(时间仓促,后面还会继续完善^_*)目前性能优化专题... 查看详情