Android 性能优化—— 启动优化提升60,2021最新大厂Android面试集合

应用程序启动有三种状态,每种状态都会影响应用程序对用户可见所需的时间:冷启动,热启动和温启动。

在冷启动时,应用程序从头开始。在其他状态下,系统需要将正在运行的应用程序从后台运行到前台。我们建议您始终根据冷启动的假设进行优化。这样做也可以改善热启动和温启动的性能。

在冷启动开始时,系统有三个任务。这些任务是:

  1. 加载并启动应用程序。

  2. 启动后立即显示应用程序空白的启动窗口。

  3. 创建应用程序进程。

一旦系统创建应用程序进程,应用程序进程就会负责下一阶段。

这些阶段是:

  1. 创建app对象.

  2. 启动主线程(main thread).

  3. 创建应用入口的Activity对象.

  4. 填充加载布局Views

  5. 在屏幕上执行View的绘制过程.measure -> layout -> draw

应用程序进程完成第一次绘制后,系统进程会交换当前显示的背景窗口,将其替换为主活动。此时,用户可以开始使用该应用程序。

因为App应用进程的创建过程是由手机的软硬件决定的,所以我们只能在这个创建过程中视觉优化。

启动主题优化


冷启动阶段 :

1. 加载并启动应用程序。

2. 启动后立即显示应用程序空白的启动窗口。

3. 创建应用程序进程。

所谓的主题优化,就是应用程序在冷启动的时候(1~2阶段),设置启动窗口的主题。

因为现在 App 应用启动都会先进入一个闪屏页(LaunchActivity) 来展示应用信息。

1.默认情况

如果我们对App没有做处理(设置了默认主题),并且在 Application 初始化了其它第三方的服务(假设需要加载2000ms),那么冷启动过程就会如下图 :系统默认会在启动应用程序的时候 启动空白窗口 ,直到 App 应用程序的入口 Activity 创建成功,视图绘制完毕。( 大概是onWindowFocusChanged方法回调的时候 )

2.透明主题优化

为了解决启动窗口白屏问题,许多开发者使用透明主题来解决这个问题,但是治标不治本。

虽然解决了上面这个问题,但是仍然有些不足。

3.设置闪屏图片主题

为了更顺滑无缝衔接我们的闪屏页,可以在启动 Activity 的 Theme中设置闪屏页图片,这样启动窗口的图片就会是闪屏页图片,而不是白屏。

这样设置的话,就会在冷启动的时候,展示闪屏页的图片,等App进程初始化加载入口 Activity (也是闪屏页) 就可以无缝衔接。

其实这种方式并没有真正的加速应用进程的启动速度,而只是通过用户视觉效果带来的优化体验。

_3_代码优化

当然上面使用设置主题的方式优化用户体验效果治标不治本,关键还在于对代码的优化。

首先我们可以统计一下应用冷启动的时间。

冷启动耗时统计


adb 命令统计

参考如何计算 App 的启动时间

http://www.androidperformance.com/2015/12/31/How-to-calculation-android-app-lunch-time/

adb命令 : adb shell am start -S -W 包名/启动类的全限定名 , -S 表示重启当前应用

更多adb命令

https://github.com/mzlogin/awesome-adb

C:\Android\Demo>adb shell am start -S -W com.example.moneyqian.demo/com.example.moneyqian.demo.MainActivity

Stopping: com.example.moneyqian.demo

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.moneyqian.demo/.MainActivity }

Status: ok

Activity: com.example.moneyqian.demo/.MainActivity

ThisTime: 2247

TotalTime: 2247

WaitTime: 2278

Complete

  1. ThisTime : 最后一个 Activity 的启动耗时(例如从 LaunchActivity - >MainActivity「adb命令输入的Activity」 , 只统计 MainActivity 的启动耗时)

  2. TotalTime : 启动一连串的 Activity 总耗时.(有几个Activity 就统计几个)

  3. WaitTime : 应用进程的创建过程 + TotalTime .

  • 在第①个时间段内,AMS 创建 ActivityRecord 记录块和选择合理的 Task、将当前Resume 的 Activity 进行 pause.

  • 在第②个时间段内,启动进程、调用无界面 Activity 的 onCreate() 等、 pause/finish 无界面的 Activity.

  • 在第③个时间段内,调用有界面 Activity 的 onCreate、onResume.

//ActivityRecord

private void reportLaunchTimeLocked(final long curTime) {

final long thisTime = curTime - displayStartTime;

final long totalTime = stack.mLaunchStartTime != 0 ?

(curTime - stack.mLaunchStartTime) : thisTime;

}

最后总结一下 : 如果需要统计从点击桌面图标到 Activity 启动完毕,可以用WaitTime作为标准,但是系统的启动时间优化不了,所以优化冷启动我们只要在意 ThisTime 即可。

系统日志统计

另外也可以根据系统日志来统计启动耗时,在Android Studio中查找已用时间,必须在logcat视图中禁用过滤器(No Filters)。因为这个是系统的日志输出,而不是应用程序的。你也可以查看其它应用程序的启动耗时。

过滤displayed输出的启动日志.

3. 代码优化


根据上面启动时间的输出统计,我们就可以先记录优化前的冷启动耗时,然后再对比优化之后的启动时间。

Application 优化

Application 作为 应用程序的整个初始化配置入口,时常担负着它不应该有的负担~

有很多第三方组件(包括App应用本身)都在 Application 中抢占先机,完成初始化操作。

但是在 Application 中完成繁重的初始化操作和复杂的逻辑就会影响到应用的启动性能

通常,有机会优化这些工作以实现性能改进,这些常见问题包括:

  • 复杂繁琐的布局初始化

  • 阻塞主线程 UI 绘制的操作,如 I/O 读写或者是网络访问.

  • Bitmap 大图片或者 VectorDrawable加载

  • 其它占用主线程的操作

我们可以根据这些组件的轻重缓急之分,对初始化做一下分类 :

  1. 必要的组件一定要在主线程中立即初始化(入口 Activity 可能立即会用到)

  2. 组件一定要在主线程中初始化,但是可以延迟初始化。

  3. 组件可以在子线程中初始化。

放在子线程的组件初始化建议延迟初始化 ,这样就可以了解是否会对项目造成影响!

所以对于上面的分析,我们可以在项目中 Application 的加载组件进行如下优化 :

将Bugly,x5内核初始化,SP的读写,友盟等组件放到子线程中初始化。(子线程初始化不能影响到组件的使用)

new Thread(new Runnable() {

@Override

public void run() {

//设置线程的优先级,不与主线程抢资源

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

//子线程初始化第三方组件

Thread.sleep(5000);//建议延迟初始化,可以发现是否影响其它功能,或者是崩溃!

}

}).start();

将需要在主线程中初始化但是可以不用立即完成的动作延迟加载(原本是想在入口 Activity 中进行此项操作,不过组件的初始化放在 Application 中统一管理为妙.)

handler.postDelayed(new Runnable() {

@Override

public void run() {

//延迟初始化组件

}

}, 3000);

闪屏页业务优化

最后还剩下那些为数不多的组件在主线程初始化动作,例如埋点,点击流,数据库初始化等,不过这些消耗的时间可以在其它地方相抵。

需求背景 : 应用App通常会设置一个固定的闪屏页展示时间,例如2000ms,所以我们可以根据用户手机的运行速度,对展示时间做出调整,但是总时间仍然为 2000ms。

闪屏页政展示总时间 = 组件初始化时间 + 剩余展示时间。

也就是2000ms的总时间,组件初始化了800ms,那么就再展示1200ms即可。

我们先了解一下 Application的启动过程,图片摘自 :

如何统计Android App启动时间

https://www.jianshu.com/p/59a2ca7df681

虽然这个以下图片的源码并不是最新源码(5.0源码),不过不影响整体流程。(7.0,8.0方法名会有所改变)。

冷启动的过程中系统会初始化应用程序进程,创建Application等任务,这时候会展示一个 启动窗口 Starting Window,上面分析了过,如果没有优化主题的话,那么就是白屏。

如果要了解更多启动过程源码,可以看我的博客 :

Launcher 启动 Activity 的工作过程

https://blog.csdn.net/qian520ao/article/details/78156214

分析源码后,我们可以知道 Application 初始化后会调用 attachBaseContext() 方法,再调用 Application 的 onCreate(),再到入口 Activity的创建和执行 onCreate() 方法。所以我们就可以在 Application 中记录启动时间。

//Application

@Override

protected void attachBaseContext(Context base) {

super.attachBaseContext(base);

SPUtil.putLong(“application_attach_time”,

System.currentTimeMillis());//记录Application初始化时间

}

有了启动时间,我们得知道入口的 Acitivty 显示给用户的时间(View绘制完毕),在博客( View的工作流程)中了解到,在onWindowFocusChanged()的回调时机中表示可以获取用户的触摸时间和View的流程绘制完毕,所以我们可以在这个方法里记录显示时间。

https://blog.csdn.net/qian520ao/article/details/78657084

//入口Activity

@Override

public void onWindowFocusChanged(boolean hasFocus) {

super.onWindowFocusChanged(hasFocus);

long appAttachTime = SPUtil.getLong(“application_attach_time”);

long diffTime = System.currentTimeMillis() - appAttachTime;//从application到入口Acitity的时间

//所以闪屏页展示的时间为 2000ms - diffTime.

}

所以我们就可以动态的设置应用闪屏的显示时间,尽量让每一部手机展示的时间一致,这样就不会让手机配置较低的用户感觉漫长难熬的闪屏页时间(例如初始化了2000ms,又要展示2000ms的闪屏页时间.),优化用户体验。

广告页优化

闪屏页过后就要展示金主爸爸们的广告页了。

因为项目中广告页图片有可能是大图,APng动态图片,所以需要将这些图片下载到本地文件,下载完成后再显示,这个过程往往会遇到以下两个问题 :

  1. 广告页的下载,由于这个是一个异步过程,所以往往不知道加载到页面的合适时机。

  2. 广告页的保存,因为保存是 I/O 流操作,很有可能被用户中断,下次拿到破损的图片。

因为不清楚用户的网络环境,有些用户下载广告页可能需要一段时间,这时候又不可能无限的等候。所以针对这个问题我们可以开启 IntentService 用来下载广告页图片。

  1. 在入口 Acitivity 中开启 IntentService 来下载广告页。 或者是其它异步下载操作。

  2. 在广告页图片 文件流完全写入后 记录图片大小,或者记录一个标识。

在下次的广告页加载中可以判断是否已经下载好

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整资料开源分享

了广告页图片以及图片是否完整,否则删除并且再次下载图片。

另外因为在闪屏页中仍然有 剩余展示时间,所以在这个时间段里如果用户已经下载好了图片并且图片完整,就可以显示广告页。否则进入主 Activity , 因为 IntentService 仍然在后台继续默默的下载并保存图片~

4. 优化效果


优化前 :

| Displayed | LaunchActivity | MainActivity |

| — | :-: | --: |

| | +2s526ms | +1s583ms |

| | +2s603ms | +1s533ms |

| | +2s372ms | +1s556ms |

优化后 :

| Displayed | LaunchActivity | MainActivity |

| — | :-: | --: |

| | +995ms | +1s191ms |

| | +911ms | +1s101ms |

| | +903ms | +1s187ms |

通过上面的几台手机的启动测试,发现优化后App冷启动的启动速度均提升了60% !!! ,并且我们可以再看一下手机冷启动时候的内存情况 :

优化前 : 伴随着大量对象的创建回收,15s内系统GC 5次。

内存使用波澜荡漾。

优化后 : 趋于平稳上升状态创建对象,15s内系统GC 2次。(后期业务拓展加入新功能,所以代码量增加。)之后总内存使用平缓下降。

**Other :**应用使用的系统不确定如何分类的内存。

**Code :**应用用于处理代码和资源(如 dex 字节码、已优化或已编译的 dex 码、.so 库和字体)的内存。

Stack : 应用中的原生堆栈和 Java 堆栈使用的内存。 这通常与您的应用运行多少线程有关。

Graphics:图形缓冲区队列向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。 (请注意,这是与 CPU 共享的内存,不是 GPU 专用内存。)

**Native :**从 C 或 C++ 代码分配的对象内存。即使应用中不使用 C++,也可能会看到此处使用的一些原生内存,因为 Android 框架使用原生内存代表处理各种任务,如处理图像资源和其他图形时,即使编写的代码采用 Java 或 Kotlin 语言。

**Java :**从 Java 或 Kotlin 代码分配的对象内存。

**Allocated :**应用分配的 Java/Kotlin 对象数。 它没有计入 C 或 C++ 中分配的对象。

更多查看 :

https://developer.android.google.cn/studio/profile/memory-profiler?hl=zh-cn

5. 启动窗口


优化完我们的代码后,分析一下启动窗口的源码。基于 android-25 (7.1.1)

启动窗口是由 WindowManagerService 统一管理的 Window窗口,一般作为冷启动页入口 Activity 的预览窗口,启动窗口由 ActivityManagerService 来决定是否显示的,并不是每一个 Activity 的启动和跳转都会显示这个窗口。

WindowManagerService 通过窗口管理策略类 PhoneWindowManager 来创建启动窗口。

拿我之前源码分析的文章中的启动流程图来看看大致 :

Launcher 启动 Activity 的工作过程

https://blog.csdn.net/qian520ao/article/details/78156214

直奔主题,在 ActivityStarter的startActivityUnchecked()方法中,调用了ActivityStack(Activity 状态管理)的startActivityLocked()方法。此时Activity 还在启动过程中,窗口并未显示。

先上一张流程图,展示了启动窗口的显示过程。

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

启动流程图来看看大致 :

Launcher 启动 Activity 的工作过程

https://blog.csdn.net/qian520ao/article/details/78156214

直奔主题,在 ActivityStarter的startActivityUnchecked()方法中,调用了ActivityStack(Activity 状态管理)的startActivityLocked()方法。此时Activity 还在启动过程中,窗口并未显示。

先上一张流程图,展示了启动窗口的显示过程。

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-YaPQSdYE-1640921171569)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: android面试大厂必考174题是一道比较常见的面试题,主要考察对Android基础知识的掌握和应用能力。 174题是关于Android中Activity的生命周期的问题。Activity是Android应用的基本组件之一,了解和掌握它的生命周期是开发Android应用的基本要求。 Activity的生命周期包括以下几个常用方法:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()等。这些方法分别在Activity的不同阶段被调用,用于管理Activity的状态和响应用户的操作。 在回答174题时,可以从以下几个方面回答: 1. 对于Activity的生命周期的理解:就是回答上述的onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()等方法的作用和调用顺序。 2. 对于Activity状态的切换:从后台到前台、从前台到后台时,Activity的生命周期方法的调用顺序是什么。 3. Activity被销毁的场景:Activity被销毁的场景有哪些,例如用户主动退出、内存不足等。 4. 懂得如何管理Activity的状态和数据:例如在onSaveInstanceState()、onRestoreInstanceState()等方法中保存和恢复Activity的状态和数据。 5. 扩展性问题:除了上述常用方法外,你还知道其他的Activity生命周期相关的方法吗?它们有什么作用? 通过以上几个方面的回答,可以较全面地回答174题。同时,结合自己在实际开发中的经验和实践,给出一些实际的例子和场景,能够更好地展示自己的理解和应用能力,提升面试效果。 ### 回答2: Android面试中,174题是一个必考的问题,考察的是对于RecyclerView中的Item动画的理解和实践经验。 RecyclerView是Android中常用的列表型控件,能够高效地展示大量数据。为了提升用户体验,我们可以为RecyclerView中的Item添加动画效果,使界面更加生动。 在实现RecyclerView的Item动画时,我们可以使用Android提供的默认动画效果,比如淡入淡出、平移、缩放、旋转等,也可以使用属性动画自定义动画效果。 当我们需要自定义动画效果时,可以通过RecyclerView.ItemAnimator类来实现。我们需要重写四个方法: 1. canReuseUpdatedViewHolder():判断是否可以复用已更新的ViewHolder。 2. animateAdd():为添加的Item添加动画效果。 3. animateMove():为移动的Item添加动画效果。 4. animateRemove():为删除的Item添加动画效果。 在实现这些方法时,我们可以使用属性动画,通过修改Item的属性值来实现平移、缩放等效果。同时,还可以配合插值器(Interpolator)来调整动画的速度曲线。 除了自定义动画效果外,我们还可以通过RecyclerView.ItemDecoration类来实现对Item的装饰效果,比如分割线、间隔等。这样可以让RecyclerView的布局更加美观和易读。 总结起来,动画在Android应用开发中起着重要的作用,可以提升用户体验。在面试中,了解RecyclerView的Item动画实现原理和实践经验是必备的。我们需要熟练掌握默认动画效果的使用,同时能够自定义动画效果,并且理解RecyclerView.ItemAnimator和RecyclerView.ItemDecoration的用法。 ### 回答3: Android面试大厂必考的174题主要针对Android开发相关的知识、技术和经验进行考察。下面我将对其中一些常见的问题进行回答。 1. 请介绍一下Activity的生命周期。 Activity的生命周期包括:onCreate()、onStart()、onResume()、onPause()、onStop()、onRestart()和onDestroy()。onCreate()在Activity被创建时调用,用于初始化Activity的状态和布局;onStart()在Activity可见但未获取焦点时调用;onResume()在Activity获取焦点时调用,此时Activity处于运行状态;onPause()在Activity失去焦点、但仍可见时调用,通常用于保存数据或释放资源;onStop()在Activity不再可见时调用,可以做一些清理工作;onRestart()在Activity重新展示时调用;onDestroy()在Activity被销毁时调用。 2. 请说明Android中的四种存储方式。 Android中的四种存储方式分别为:SharedPreferences、文件存储、数据库存储和网络存储。 - SharedPreferences是一种轻量级的存储方式,用于存储少量的键值对数据,适用于存储一些简单的配置信息。 - 文件存储用于存储大量的非结构化数据,可以使用FileOutputStream和FileInputStream进行读写操作。 - 数据库存储使用SQLite数据库来持久化保存结构化数据,可以通过SQLiteOpenHelper来创建和管理数据库。 - 网络存储通过与服务器进行通信,将数据存储在服务器上,常见的方式有HTTP请求和WebSocket。 3. 请解释一下Android中的消息机制。 Android中的消息机制主要用于不同组件之间的通信和线程间的通信。它基于“消息队列”和“消息循环”的概念,核心类为Handler、Message和Looper。 - Handler:Handler负责发送和处理消息,通过sendMessage()方法发送消息,通过handleMessage()方法处理消息。 - Message:Message对象封装了消息的内容,包括消息类型和数据。 - Looper:Looper用于管理MessageQueue,不断从MessageQueue中取出消息并将其分发到对应的Handler进行处理。 通过使用消息机制,可以实现不同线程之间的通信,例如在子线程中下载数据完成后,使用Handler将结果传递给主线程进行UI更新。 以上是对部分Android面试大厂必考的174题的回答,希望能对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值