美文网首页
LCODER性能优化:启动优化

LCODER性能优化:启动优化

作者: _Jun | 来源:发表于2022-08-27 15:15 被阅读0次

    一、 APP启动流程简析

    1. 点击桌面APP图标,Launcher进程采用Binder IPC向System_server进程发起startActivity请求。
    2. system_server进程接收到请求后,向zygote进程发送创建进程的请求。
    3. Zygote进程fork出新的子进程,即APP进程。
    4. APP进程,通过Binder IPC向System_server进程发起attachApplication请求。
    5. system_server进程在收到请求后,进行一系列准备工作后,再通过Binder IPC向APP进程发送scheduleLaunchActivity请求。
    6. APP进程的bind线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息。
    7. 主线程接收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。
    8. 到此,APP便正式启动,开始进入Activity生命周期,执行完onCreate等方法,UI渲染结束后便可看到APP主界面。

    二、查看启动时间

    2.1 启动状态

    冷启动 应用从头开始启动:系统进程在冷启动后才创建应用进程,发送冷启动的情况包括应用自设备启动后或系统终止应用后首次启动。

    热启动 系统将Activity带到前台的过程。

    温启动 温启动包含了在冷启动期间发生的部分操作,同时,它的开销比热启动高。有许多潜在状态可视为温启动,如:

    1. 用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行onCreate()从头开始重新创建Activity。
    2. 系统将应用从内存中释放,然后又重新启动它, 但传递到onCreate()的已保存的实例savedInstanceState对于完成此任务有一定的助益。

    我们一般优化的是冷启动的过程。优化冷启动的性能,也可以提升温启动和热启动的性能。

    2.2 启动时间多少秒,需要做性能优化呢?

    在性能测试中存在启动时间2-5-8原则:

    • 当用户能够在2秒以内得到响应时,会感觉系统的响应很快;
    • 当用户在2-5秒之间得到响应时,会感觉系统的响应速度还可以;
    • 当用户在5-8秒以内得到响应时,会感觉系统的响应速度很慢,但是还可以接受;
    • 而当用户在超过8秒后仍然无法得到响应时,会感觉系统糟透了,或者认为系统已经失去响应

    而Google 也提出一项计划:Android Vitals 。该计划旨在改善 Android 设备的稳定性和性能。当选择启用了该计划的用户运行您的应用时,其 Android 设备会记录各种指标,包括应用稳定性、应用启动时间、电池使用情况、呈现时间和权限遭拒等方面的数据。GooglePlay管理中心会汇总这些数据,并将其显示在AndroidVitals信息中心 内。

    当应用启动时间过长时,Android Vitals 可以通过 Play 管理中心提醒您,从而帮助提升应用性能。Android Vitals

    在您的应用出现以下情况时将其启动时间视为过长:

    • 冷启动用了 5 秒或更长时间。
    • 温启动用了 2 秒或更长时间。
    • 热启动用了 1.5 秒或更长时间。

    2.3 查看启动时间

    方式一: 直接使用AndroidStudio查看
    在Android4.4以上,在APP启动的过程中,直接在logcat中输入关键字“Displayed”查找。 此值代表从启动进程到在屏幕上完成对应Activity绘制所用的时间。

    可以看到启动顺序: WelcomePageActivity 用时 996ms --> LoginPageActivity 用时 549ms --> MainActivity 用时 1s381ms

    方式二: 使用adb命令统计

    adb shell am start -S -W[packageName]/[activityName]

    输出:

    ThisTime:1143
    TotalTime:1143
    WaitTime:1169

    以上三个时间分别指的是:
    ThisTime: 表示一连串启动Activity的最后一个Activity的启动耗时。
    TotalTime:表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause 的耗时。
    WaitTime: 总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间。

    这个时间是统计到onWindowFoucsChanged()方法。

    三、使用工具分析

    使用AndroidStudio自带的工具CPU Profile. 记录启动过程,其实就是记录在启动过程中CPU的活动情况,要在应用的启动过程中自动开始记录CPU的活动,需要执行以下操作:

    1. 在AndroidStudio中选择 Run > Edit Confifigurations。
    1. 设置Profiling选项

    上图中,在start this recording on startup中有四个选项:

    Sample Java Methods : 对Java方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈。分析器会比较捕获的数据集,以推导与应用的Java代码执行有关的时间和资源使用信息。如果应用在捕获调用堆栈后进入一个方法并在下次捕获前退出该方法,分析器将不会记录该方法调用。如果您想要跟踪生命周期如此短的方法,应使用检测 跟踪。

    Trace Java Methods: 跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这 些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。

    Sample C/C++ Functions: 对 C/C++ 函数采样:捕获应用的原生线程的采样跟踪数据。要使用此配置,您必须将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

    Trace System Calls: 跟踪系统调用:捕获非常翔实的细节,以便您检查应用与系统资源的交互情况。您可以检查线程状态的确切 时间和持续时间、直观地查看所有内核的 CPU 瓶颈在何处,并添加要分析的自定义跟踪事件。要使用此配 置,您必须将应用部署到搭载 Android 7.0(API 级别 24)或更高版本的设备上。 此跟踪配置在 systrace 的基础上构建而成。您可以使用 systrace 命令行实用程序指定除 CPU Profiler 提供的 选项之外的其他选项。systrace 提供的其他系统级数据可帮助您检查原生系统进程并排查丢帧或帧延迟问 题。

    按照自己需要选择就好,一般来说,Android开发者只需要关注前两个选项即可。

    1. Run > Profifile,将您的应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。

    点击stop,结束跟踪。

    上图中显示的表格是Call Chart,该表的特色是以图形来呈现方法跟踪数据或函数跟踪数据,其中调用的时间段和时间在横轴上表示,而其被调用方则在纵轴上显示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色。

    该表的横轴代表时间线,越宽说明耗时越久,结合上图看到的是 MyApplication中的oncreate() 方法,耗时2.52s。

    这个表中的数据能大致的告诉我们方向,如果想要知道方法中具体耗时最多的部分,还要看下一个表:Flame Chart,俗称火焰图。

    火焰图提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或函数收集起来,并在火焰图中将它们表示为一个较长的横条。

    横轴显示的是百分比数值。由于忽略了时间线信息,Flame Chart 可以展示每次调用消耗时间占用整个记录时长的 百分比。 同时纵轴也被对调了,在顶部展示的是被调用者,底部展示的是调用者。此时的图表看起来越往上越窄, 就好像火焰一样,因此得名: 火焰图。

    也就是把Call Chart中的上下调用栈倒过来显示。

    上图中可以看到,耗时最久的方法是:SysInitVerificationCodeSize(),耗时:2.02s

    Top Down Tree

    这是一个调用列表,在该列表中展开方法或函数节点会显示它调用了的方法节点。

    上图中,我们也可以看出来耗时最久的方法是:SysInitVerificationCodeSize() 耗时:2.018773s, 所以我们也可以通过这个列表获取更精确的时间。

    对于每个节点,都有三个时间信息,分别是:

    • Self Time —— 运行自己的代码所消耗的时间;
    • Children Time —— 调用其他方法的时间;
    • Total Time —— 前面两者时间之和。

    Bottom Up Tree

    方便地找到某个方法的调用栈。在该列表中展开方法或函数节点会显示哪个方法调用了自己。

    通过上面的分析,可以得知代码中最耗时的方法是:SysInitVerificationCodeSize()SysInitUserAgentInfo,经过分析得知,这两个方法确实是比较耗时的操作,是so库中的代码,因为加了sys同步锁,导致执行该方法时,需要等待同类sys方法执行完成后才可以执行。所以我们优化一下代码,把这两个方法放到子线程中去。再重复上面的操作得到:

    启动速度降低到430ms。

    四、使用Debug API生成trace文件

    除了使用Profile启动之外,我们还可以借助Debug API生成trace文件。

    public class MyApplication extends Application {
    
     @Override
        public void onCreate() {
            Debug.startMethodTracing("KimLiu");
        }
    
    }
    
    public class MainActivity extends AppCompatActivity {
    
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Debug.stopMethodTracing();
    }
    
    }
    

    运行APP,会生成一个KimLiu.trace文件,将手机中的trace文件保存到电脑中,再使用AndroidStudio打开即可。

    使用AndroidStudio打开后发现,和直接使用Profile分析是一样的效果,同样也得出了SysInitVerificationCodeSize() 是最耗时的方法, 和Profile一样分析即可。

    作者:KimLiu
    链接:https://juejin.cn/post/7135358031033794590

    相关文章

      网友评论

          本文标题:LCODER性能优化:启动优化

          本文链接:https://www.haomeiwen.com/subject/meefnrtx.html