前言:关于app的启动方式,一关有三种,分别是冷启动,热启动,温启动。
冷启动:系统内部没有该应用的进程,会完成一整套的进程分配到后续UI渲染等的完整流程,耗时也是最久的。
热启动:系统后台还保留着该应用的进程。如果应用程序的所有activity存在内存中,则应用程序可以避免重复对象初始化、渲染、绘制操作.。
如:app按了home键再返回回应用
温启动:温启动包含了冷启动的一些操作,由于app进程依然在。activity 不在内存中。消耗在冷启动跟热启动之间。
如 app一直按back键,直到回到桌面
优化方式:
1 视觉欺骗:
Android应用在点击launcher图标后,首先会启动一个主题窗口,在低版本的时候这个主题色是黑色的,而现在是白色,就是所谓黑白屏。这个主题窗口一直存在直到第一个activity的页面渲染出来,才会消失。所以如果不进行任何的处理,白屏时间稍微长一点,就会给用户带来很不好的体验。为了解决这个问题常规的方法有以下几种。
A 方案:将AndroidManifes文件中Application标签下的AppTheme进行修改,直接将预览页面去掉或者改为透明
<item name="android:windowDisablePreview">true</item> <item name="android:windowIsTranslucent">true</item>
缺点:这种方式会在点击图标后,会停顿一下,直到activity页面渲染出来,会给人一种卡顿的感觉。
B 方案:新增首个Activity的主题,设置背景图片
<style name="AppTheme.Launcher1"> <item name="android:windowBackground">@mipmap/ic_launcher</item> </style> @Override protected void onCreate(@Nullable Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash);}
缺点:不仅仅需要在配置文件中进行配置,还需要对acitivty中对主题进行设置;另外单纯的设置图标在不同屏幕的设备下可能会导致拉伸等效果,无法保证很好的适配。
C 方案:终极方案,最推荐使用,B方案的改良,将图片背景该为布局背景,另外还可以加上activity的渐入效果(网页云音乐app方案)
<style name="AppTheme.LauncherTheme2"> <item name="android:windowNoTitle">true</item> <item name="android:windowFullscreen">true</item> <item name="android:windowBackground">@drawable/splash_preview</item> </style>
splash_preview.xml
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@android:color/holo_red_light"></item> <item android:top="30dp"> <bitmap android:gravity="top" android:src="@drawable/ic_launcher_round"></bitmap> </item> </layer-list>
SplashActivity 页面什么都不做,只单纯放一张图片,这样会有一个图片渐入的效果,不清楚的同学可以打开网易云音乐看看启动。
优点:布局中以颜色为背景,不会担心拉伸等情况,在配上内部的小图片也很简洁。
2 启动时间缩短
A 首先要明白app启动做了什么事?
这里简单说明下,launcher进程向AMS进程发出启动acitivity的请求,AMS 又通过socket通信向zogote进程发出申请开启应用进程的请求,应用进程启动后,会依次去启动application,launcher标签的activity,直至第一个acitivty的页面完成渲染,我们才开始可以看到应用界面。
B 计算启动时间,针对性分析!
1 查看logcat日志
获取启动时间1: Displayed com.mnstartopt/.SplashActivity: +955ms 通过系统获取启动时间
2 通过adb 命令启动查看输出
adb命令获取启动时间2: adb shell am start -W com.mnstartopt/.SplashActivity // launch:COLD,WARM,HOT // ThisTime: 925 代表最后一个Activity启动时间; // TotalTime:925 代表所有Activity的启动时间; // WaitTime: 977 所有时间:ams启动activity总耗时;
3 自己写调试代码
public class LauncherTimer { public static long startTime; public static void logStart(){ startTime = System.currentTimeMillis(); } public static void logEnd(String tag){ Log.d("time",tag+" launcher time = "+(System.currentTimeMillis()-startTime)); } }
分别在Application的oncreat(), activity的onResume(),跟onWindowFocusChanged()方法输出。
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); // 首帧开始绘制的时候会回调; LauncherTimer.logEnd("Tag2"); }
C查看重要方法内部,各部分执行消耗的时间。
在需要查看的方法内部,执行前调用
Debug.startMethodTracing("Launcher");
方法结束的地方执行:
Debug.stopMethodTracing();
当app运行之后会在sdcard 对于应用目录生成指定的文件,通过查看文件可以看到里面每个阶段执行消耗的时间。
D 针对性优化
1 在applciation内部使用线程池,将一些非必要同步启动的方法配置放入子线程中进行执行。
2 简化launcher页面的布局,减少布局层级,加快初始的显示。
3 将application和launcher标签的activity的onCreate 和onResume减少耗时久的操作,或者将庞大的资源/逻辑 进行拆分。查找哪块逻辑能不能进行 空间换时间。