随着项目版本的迭代,App的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关,从本篇文章开始,我将开启一个Android应用性能优化的专题,从理论到实战,从入门到深挖,手把手将性能优化实践到项目中,欢迎持续关注!
背景资料
应用的启动分为冷启动、热启动、温启动,而启动最慢、挑战最大的就是冷启动:系统和App本身都有更多的工作要从头开始! 应用在冷启动之前,要执行三个任务:
- 加载启动App;
- App启动之后立即展示出一个空白的Window;
- 创建App的进程;
而这三个任务执行完毕之后会马上执行以下任务:
- 建App对象;
- 启动Main Thread;
- 创建启动的Activity对象;
- 加载View;
- 布置屏幕;
- 进行第一次绘制;
而一旦App进程完成了第一次绘制,系统进程就会用Main Activity替换已经展示的Background Window,此时用户就可以使用App了。
同样,Google也给出了启动加速的方向:
- 利用提前展示出来的Window,快速展示出来一个界面,给用户快速反馈的体验;
- 避免在启动时做密集沉重的初始化(Heavy app initialization);
- 定位问题:避免I/O操作、反序列化、网络操作、布局嵌套等。
可以通过以下命令测试启动耗时
adb shell am start -W com.titan.asfh/com.titan.asfh.login.LoginActivity
命令含义:
ThisTime:最后一个启动的Activity的启动耗时;
TotalTime:自己的所有Activity的启动耗时;
WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)
常见问题及优化方式
启动主题切换
按照官方文档的说明:使用Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawable。 Layout XML file:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"> <item android:drawable="@android:color/white"/> <item> <bitmap android:src="@mipmap/logo" android:gravity="center"/> </item> </layer-list>
Style:
<style name="AppTheme.launcher" parent="AppTheme.NoTitle"> <item name="android:windowBackground">@drawable/launcher </item> </style>
Manifest file:
<?xml version="1.0" encoding="utf-8"?> <activity android:name=".login.LoginActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.launcher" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Activity:
public class LoginActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.AppTheme_NoTitle); super.onCreate(savedInstanceState); } }
实际测试发现,启动确实避免启动白屏的问题,但是无论时冷启动还是热启动都会有背景图,实测QQ、微信不是这样,判断 它们是通过增加Splash Activity实现的。
此外这样也可以解决白屏问题
<style name="AppTheme.launcher" parent="android:Theme.Translucent.NoTitleBar.Fullscreen"/>
Avoid Heavy App Initialization
当代码覆盖Application 对象时,启动性能会受到影响,并在初始化该对象时执行繁重的工作或复杂的逻辑。如果您的应用程序子类执行不需要完成的初始化,您的应用程序可能会浪费时间在启动过程中。一些初始化可能是完全不必要的:例如,当应用程序实际启动以响应意图时,初始化主要活动的状态信息。意图是,应用程序只使用以前初始化的状态数据的一个子集。
应用程序初始化过程中的其他挑战包括垃圾收集事件的影响或数量众多,或磁盘I / O与初始化同时发生,进一步阻止初始化过程。垃圾收集尤其是Dalvik运行时的一个考虑因素; Art运行时同时执行垃圾收集,最大限度地减少操作的影响。
诊断问题
您可以使用方法跟踪或内联跟踪来尝试诊断问题。
方法追踪 运行方法跟踪器工具显示该 callApplicationOnCreate() 方法最终调用您的com.example.customApplication.onCreate 方法。如果该工具显示这些方法需要很长时间才能完成执行,那么您应该进一步研究以查看正在进行的工作。
内联追踪 使用内联追踪来调查可能的罪魁祸首,包括: 你的应用程序的初始onCreate() 功能。 任何全球单身人士对象您的应用程序初始化。 任何磁盘I / O,反序列化,或瓶颈期间可能发生的紧密循环。
解决问题的办法 无论问题出在不必要的初始化还是磁盘I / O,解决方案都会调用延迟初始化对象:只初始化那些立即需要的对象。例如,不是创建全局静态对象,而是移动到单例模式,应用程序只在第一次访问对象时创建对象。另外,考虑使用像Dagger 这样的依赖注入框架来创建对象,并且依赖关系是当它们被首次注入时。
Heavy activity initialization
活动创建通常需要大量的高开销工作。通常,有机会优化这项工作来实现性能改进。这些常见问题包括:
膨胀大型或复杂的布局。 阻塞磁盘或网络I / O的屏幕绘图。 加载和解码位图。 栅格化VectorDrawable对象。 初始化活动的其他子系统。 诊断问题 在这种情况下,方法跟踪和内联跟踪也是有用的。
方法追踪 当运行Method Tracer工具时,特定的区域将关注于您的应用程序的Application子类构造函数和 com.example.customApplication.onCreate()方法。
如果该工具显示这些方法需要很长时间才能完成执行,那么您应该进一步研究以查看正在进行的工作。
内联追踪 使用内联追踪来调查可能的罪魁祸首,包括:
你的应用程序的初始onCreate() 功能。 它初始化的任何全局单例对象。 任何磁盘I / O,反序列化,或瓶颈期间可能发生的紧密循环。 解决问题的办法 有很多潜在的瓶颈,但是两个常见的问题和解决办法如下:
视图层次越大,应用所需的时间就越多。你可以采取两个步骤来解决这个问题: 通过减少冗余或嵌套布局来平整您的视图层次结构。 不要在启动时不需要显示部分UI。相反,使用ViewStub对象作为应用程序可以在更适当的时间膨胀的子层次结构的占位符。 在主线程上完成所有的资源初始化操作也会减慢启动速度。你可以解决这个问题如下: 移动所有的资源初始化,以便应用程序可以在不同的线程上执行它。 允许应用程序加载并显示您的视图,然后更新依赖于位图和其他资源的视觉属性。