APP启动慢怎么办,Android官方这样说

CleBender 8年前
   <p>这篇文章从干货总量方面不如第一篇,而且一连发了两篇类似文档,总感觉没有进步与新意。但是后来琢磨了一下,这篇质量也不差,APP启动时间也是此次项目我新碰到的一个点,估计也有相当多的同学碰到这个问题。之前并没有太在意这个点,网上也已经有比较好的文章来解决这个问题。但是还是跟第一篇的原因类似,官方的文章还是从原理上分析的比较好, <em>不仅授之以鱼,还授之以渔</em> 。所以还是想翻过来分享给大家,希望多指教,后面会丰富文章的类型,分享更多更好的文章。</p>    <h2><strong>启动时间性能</strong></h2>    <p>用户期望应用程序响应速度快,加载速度快。 启动时间较慢的应用程序不满足此预期,可能会令用户失望。 这种糟糕的体验可能会导致用户在应用商店上差评你的应用,甚至完全放弃你的应用。</p>    <p>本文档提供了有助于你优化应用程序启动时间的信息。 它首先解释启动过程的内部。 接下来,它讨论如何配置启动性能。 最后,它描述了一些常见的启动时间问题,并提供了一些提示如何解决。</p>    <h3><strong>启动内部</strong></h3>    <p>应用程序启动可以在三个状态之一发生,每个状态都会影响应用程序对用户可见所需的时间:冷启动,热启动和温启动。 在冷启动,你的应用程序从头开始。 在其他状态下,系统需要将应用程序从后台运行到前台。 我们建议你总是基于冷启动的假设进行优化。 这样做可以提高热启动和温启动的性能。</p>    <p>为了优化你的应用程序以便快速启动,有必要了解系统和应用程序级别上发生的情况,以及这些状态和在这些状态中的交互方式。</p>    <p><strong>冷启动</strong></p>    <p>冷启动指的是应用程序从头开始:系统中没有你的应用程序进程,直到此开始,才创建了你的应用程序进程。 在应用程序自启动以来第一次启动或系统终止应用程序等情况下会发生冷启动。 这种类型的启动在最小化启动时间方面是最大的挑战,因为系统和应用程序比其他启动状态要做更多的工作。</p>    <p>在冷启动的开始,系统有三个任务。这些任务是:</p>    <ol>     <li>加载和启动应用程序。</li>     <li>在启动后立即显示应用程序的空白开始窗口。</li>     <li>创建 应用程序进程 。</li>    </ol>    <p>一旦系统创建应用程序进程,应用程序进程将负责下一阶段。 这些阶段是:</p>    <ol>     <li>创建应用程序对象</li>     <li>创建主Activity</li>     <li>填充视图</li>     <li>放置屏幕</li>     <li>执行初始绘制</li>    </ol>    <p>一旦应用进程完成了第一次绘制,系统进程就会替换当前显示的背景窗口,将其替换为主Activity。此时,用户可以开始使用应用程序。</p>    <p>图1显示了系统和应用程序进程如何在彼此之间移交工作。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/cff53c10a51e8fcb5601e837d9caf907.png"></p>    <p style="text-align: center;">图1.冷应用程序启动的重要部分的可视化表示。</p>    <p>在创建应用程序和创建activity时可能会出现性能问题。</p>    <p><strong>应用程序创建</strong></p>    <p>当应用程序启动时,空白的开始窗口保留在屏幕上,直到系统第一次完成绘制应用程序。 此时,系统过程将切换应用程序的开始窗口,允许用户开始与应用程序交互。</p>    <p>如果你在自己的应用程序中重载了 Application.oncreate( ) ,应用程序将通过在你的应用程序对象上调用此方法来启动。 之后,应用程序产生主线程,也称为UI线程,并创建你的主activity的任务。</p>    <p>从此刻开始,系统级和应用级进程将根据应用程序生命周期阶段进行。</p>    <p><strong>Activity创建</strong></p>    <p>应用程序进程创建你的activity后,该activity将执行以下操作:</p>    <ol>     <li>初始化值。</li>     <li>调用构造函数。</li>     <li>调用适用于activity的当前生命周期状态的回调方法,例如 Activity.onCreate( ) 。</li>    </ol>    <p>通常, onCreate( ) 方法对加载时间的影响最大,因为它以最高的开销执行工作:加载和扩充视图,并初始化活动运行所需的对象。</p>    <h3><strong>热启动</strong></h3>    <p>与冷启动相比,热启动应用程序要简单得多,开销更低。 在热启动,所有的系统都是把你的activity推到前台。 如果所有应用程序的activity仍驻留在内存中,那么应用程序可以避免重复对象初始化,布局膨胀和呈现。</p>    <p>但是,如果某些内存已经响应内存调整事件(例如 onTrimMemory( ) )而被清除,那么将需要根据热启动事件重新创建这些对象。</p>    <p>热启动显示与冷启动场景相同的屏幕行为:系统进程显示空白屏幕,直到应用程序完成呈现活动。</p>    <h3><strong>温启动</strong></h3>    <p>温启动包括在冷启动期间发生的操作的一些子集;同时,它表示比热启动少的开销。有许多潜在的状态可以被认为是温暖的开始。例如:</p>    <ul>     <li> <p>用户退出你的应用,但随后重新启动它。该过程可能已继续运行,但应用程序必须通过调用 onCreate( ) 从头开始重新创建活动。</p> </li>     <li> <p>系统从内存中退出你的应用程序,然后用户重新启动它。进程和Activity需要重新启动,但任务可以从保存的实例状态包传递到 onCreate( ) 中受益。</p> </li>    </ul>    <h3><strong>分析启动性能</strong></h3>    <p>为了正确诊断开始时间性能,你可以跟踪显示应用程序启动所需时间的指标。</p>    <p><strong>初始显示时间</strong></p>    <p>从Android 4.4(API级别19),logcat包括一个输出行,包含一个名为 Displayed 的值。 此值表示在启动过程和完成在屏幕上绘制相应活动之间经过的时间量。 经过的时间包括以下事件序列:</p>    <ol>     <li>启动过程。</li>     <li>初始化对象。</li>     <li>创建并初始化活动。</li>     <li>膨胀布局。</li>     <li>第一次绘制你的应用程序。</li>    </ol>    <p>报告的日志行看起来类似于以下示例:</p>    <pre>  ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms</pre>    <p>如果你正在从命令行或终端中跟踪logcat输出,则查找已用时间很简单。 要在Android Studio中查找耗用时间,必须在logcat视图中禁用过滤器。 禁用过滤器是必要的,因为系统服务器会(而不是应用程序本身)提供此日志。</p>    <p>完成相应设置后,你可以轻松搜索正确的字词以查看时间。图2显示了如何禁用过滤器,并在底部的第二行输出中显示了显示时间的logcat输出示例。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/74eb9a06a6b5182a24b4b440d70f01a4.png"></p>    <p style="text-align: center;">图2.禁用过滤器,并在logcat中查找显示的值。</p>    <p>logcat输出中 Displayed 指标不一定捕获所有资源加载和显示之前的时间量:它不包括布局文件中未引用的资源,或者应用程序作为对象初始化的一部分创建的资源。 它排除了这些资源,因为加载它们是一个内联过程,并且不会阻止应用程序的初始显示。</p>    <p>完全显示的时间</p>    <p>你可以使用 reportFullyDrawn( ) 方法来测量应用程序启动和完成显示所有资源和查看层次结构之间的经过时间。 这在应用程序执行延迟加载的情况下可能是有价值的。 在延迟加载中,应用程序不会阻止窗口的初始绘制,而是异步加载资源并更新视图层次结构。</p>    <p>如果由于延迟加载,应用程序的初始显示不包括所有资源,那么你可能会将所有资源和视图的完成加载和显示视为单独的指标:例如,你的UI可能已完全加载, 但尚未显示应用程序必须从网络获取的图片。</p>    <p>为了解决这个问题,你可以手动调用 reportFullyDrawn( ) ,让系统知道你的活动已完成延迟加载。 当你使用此方法时,logcat显示的值是自应用程序对象创建以来所经过的时间,以及调用 reportFullyDrawn( ) 的时刻。</p>    <p>如果你发现你的显示时间比你想要的慢,你可以继续尝试找出启动过程中的瓶颈</p>    <p><strong>找到瓶颈</strong></p>    <p>找到瓶颈的两个好方法是Android Studio的Method Tracer工具和内联跟踪。要了解Method Tracer,请参阅该工具的 documentation 。</p>    <p>如果你无法访问Method Tracer工具,或无法在正确的时间启动该工具以获取日志信息,则可以通过在应用程序和活动的 onCreate( ) 方法中内联跟踪获得类似的洞察。 要了解内联跟踪,请参阅 Trace 函数的参考文档以及 Systrace 工具。</p>    <h3><strong>常见问题</strong></h3>    <p>本节讨论了经常影响应用程序启动性能的几个问题。这些问题主要涉及初始化应用程序和activity对象,以及加载屏幕。</p>    <p><strong>繁重的应用程序初始化</strong></p>    <p>当代码覆盖 Application 对象时,启动性能可能会受到影响,并且在初始化该对象时执行繁重的工作或复杂的逻辑。 如果你的应用程序子类执行不需要完成的初始化,你的应用程序可能会在启动期间浪费时间。 一些初始化可能是完全不必要的:例如,当应用实际上响应于intent而启动时,初始化主activity的状态信息。 根据intent,应用程序仅使用之前初始化的状态数据的子集。</p>    <p>应用程序初始化期间的其他挑战包括影响大或数量众多的垃圾收集事件,或磁盘I / O与初始化同时发生,进一步阻止初始化过程。 垃圾收集是Dalvik运行时特别考虑的问题; Art运行时同时执行垃圾回收,最小化操作的影响。</p>    <p><strong>诊断问题</strong></p>    <p>你可以使用方法跟踪或内联跟踪来尝试诊断问题。</p>    <p><strong>方法跟踪</strong></p>    <p>运行Method Tracer工具会显示 callApplicationOnCreate( ) 方法最终调用 com.example.customApplication.onCreate 方法。 如果工具显示这些方法需要很长时间才能完成执行,那么您应该进一步探索以了解正在发生的工作。</p>    <p><strong>内联跟踪</strong></p>    <p>使用内联跟踪来调查可能的罪魁祸首,包括:</p>    <ul>     <li>你应用程序的初始 onCreate( ) 函数。</li>     <li>你的应用程序初始化的任何全局单例对象。</li>     <li>任何磁盘I / O,反序列化或在瓶颈期间可能发生的紧密循环。</li>    </ul>    <p><strong>解决问题</strong></p>    <p>无论问题是不必要的初始化还是磁盘I / O,解决方案都需要延迟初始化对象:只初始化那些立即需要的对象。 例如,而不是创建全局静态对象,而是移动到单例模式,应用程序只在首次访问它们时才会初始化对象。</p>    <p><strong>繁重的Activity初始化</strong></p>    <p>Activity创建通常需要很多高开销的工作。通常,有机会优化这项工作以实现性能提高。这些常见问题包括:</p>    <ul>     <li>填充大的或复杂的布局。</li>     <li>阻止在磁盘或网络I / O上绘制屏幕。</li>     <li>加载和解码位图。</li>     <li>栅格化 VectorDrawable 对象。</li>     <li>初始化activity的其他子系统。</li>    </ul>    <p><strong>诊断问题</strong></p>    <p>在这种情况下,同样,方法跟踪和内联跟踪都可以证明是有用的。</p>    <p><strong>方法跟踪</strong></p>    <p>当运行Method Tracer工具时,特定区域将专注于你的应用程序的 Application 子类构造函数和 com.example.customApplication.onCreate() 方法。</p>    <p>如果工具显示这些方法需要很长时间才能完成执行,那么你应该进一步探索以了解正在发生的工作。</p>    <p><strong>内联跟踪</strong></p>    <p>使用内联跟踪来调查可能的罪魁祸首,包括:</p>    <ul>     <li>你应用程式的初始 onCreate( ) 函数。</li>     <li>它初始化的任何全局单例对象。</li>     <li>任何磁盘I / O,反序列化或在瓶颈期间可能发生的紧密循环。</li>    </ul>    <p><strong>解决问题</strong></p>    <p>有许多潜在的瓶颈,但两个常见的问题和补救措施如下:</p>    <p>你的视图层次结构越大,应用程序填充的时间就越长。 你可以采取两个步骤来解决此问题:</p>    <ul>     <li> <p>通过减少冗余或嵌套布局来展开视图层次结构。</p>      <ol>       <li>不要对UI的不需要在启动过程中可见的部分进行填充。</li>       <li>相反,使用 ViewStub 对象作为子层次结构的占位符,应用程序可以在更适当的时间填充。</li>      </ol> </li>     <li> <p>将所有的资源初始化在主线程上也可以减慢启动速度。 你可以解决这个问题,如下所示:</p>      <ol>       <li>移动所有资源初始化,以便应用程序可以在不同的线程上懒惰地执行它。</li>       <li>允许应用加载和显示你的视图,然后更新依赖于位图和其他资源的可视属性。</li>      </ol> </li>    </ul>    <p><strong>主题化启动屏幕</strong></p>    <p>你可能希望为应用程序的加载体验设计主题,以便应用程序的启动屏幕与应用程序的其他部分保持一致,而不是与系统主题一致。这样做可以隐藏缓慢的活动启动。</p>    <p>实现主题启动屏幕的常见方法是使用 windowDisablePreview 属性关闭系统进程在启动应用程序时绘制的初始空白屏幕。 但是,此方法可能会导致启动时间比不抑制预览窗口的应用程序更长。 此外,它强制用户在activity启动时等待而不反馈,会使他们怀疑应用程序是否正常工作。</p>    <p><strong>诊断问题</strong></p>    <p>你可以通过观察用户启动应用时的慢响应来诊断此问题。在这种情况下,屏幕可能看起来已冻结,或已停止响应输入。</p>    <p><strong>解决问题</strong></p>    <p>我们建议你不要禁用预览窗口,而是遵循常见的 Material Design 。你可以使用activity的 windowBackground 主题属性为起始activity提供一个简单的自定义drawable。</p>    <p>例如,你可以创建一个新的可绘制文件,并从布局XML和应用程序清单文件中引用它,如下所示:</p>    <p>Layout XML 文件:</p>    <pre>  <layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">    <!-- The background color, preferably the same as your normal theme -->    <item android:drawable="@android:color/white"/>    <!-- Your product logo - 144dp color version of your app icon -->    <item>      <bitmap        android:src="@drawable/product_logo_144dp"        android:gravity="center"/>    </item>  </layer-list></pre>    <p>Manifest 文件:</p>    <pre>  <activity ...  android:theme="@style/AppTheme.Launcher" /></pre>    <p>转换回正常主题的最简单方法是在调用 super.onCreate() 和 setContentView() 之前调用 setTheme(R.style.AppTheme) :</p>    <pre>  public class MyMainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {      // Make sure this is before calling super.onCreate      setTheme(R.style.Theme_MyApp);      super.onCreate(savedInstanceState);      // ...    }  }</pre>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/42aea2335019</p>    <p> </p>