当我们开发一个比较大型的APP时,在Application做了很多初始化工作,然后SplashActivity中又做了很多工作,我们点击桌面启动APP时,会发现要很久才能启动起来,这时测试跟产品就会说,怎么APP启动这么慢啊,得想想办法优化啊…哈哈,作为开发的我们,得苦逼得想办法优化了,下面让我讲讲我在工作中对启动APP做的一些优化。
我们知道APP启动,其实有两种方式,一种是冷启动,另一种是热启动。
启动的两种方式
-
1.冷启动:当直接从桌面上直接启动,同时后台没有该进程的缓存,这个时候系统就需要重新创建一个新的进程并且分配各种资源。这个是最耗时的一种启动方式,这个也是我们作重要优化的。
-
2.热启动:该app后台有该进程的缓存,这时候启动的进程就属于热启动。热启动不需要重新分配进程,也不会走Application了,直接走的就是app的入口Activity,这样就速度快很多,就是我们平时按HOME 键退回到桌面,然后点击app桌面图标启动APP,这时你会发现启动的很快,这个就是属于热启动。
当我们感觉APP启动很慢时,你是不是会想,我怎么知道他启动是多慢,这时你得想办法测量出APP从开始点击桌面图标到完全启动到底花了多长时间。
如何测量一个应用的启动时间。
使用命令行来启动app,同时进行时间测量。单位:毫秒adb shell am start -W [PackageName]/[PackageName.MainActivity]adb shell am start -W com.gzsll.hupu/.ui.splash.SplashActivityadb shell am start -W com.example.applicationstartoptimizedemo/com.example.applicationstartoptimizedemo.SplashActivityadb shell am start -W com.dn.splashoptimize/com.dn.splashoptimize.MainActivity复制代码
以下是测试的结果试例:
ThisTime: 165 指当前指定的MainActivity的启动时间TotalTime: 165 整个应用的启动时间,Application+Activity的使用的时间。WaitTime: 175 包括系统的影响时间---比较上面大。复制代码
应用启动的流程
Application从构造方法开始--->attachBaseContext()--->onCreate()Activity构造方法--->onCreate()--->设置显示界面布局,设置主题、背景等等属性--->onStart()--->onResume()--->显示里面的view(测量、布局、绘制,显示到界面上) 时间花在哪里了?app启动的耗时主要是在:
Application初始化 + MainActivity的界面加载绘制时间。减少应用的启动时间的耗时
- 1)不要在Application的构造方法、attachBaseContext()、onCreate()里面进行初始化耗时操作。
- 2)MainActivity,由于用户只关心最后的显示的这一帧,对我们的布局的层次要求要减少,自定义控件的话测量、布局、绘制的时间。不要在onCreate、onStart、onResume当中做耗时操作。
-
3)对于SharedPreference的初始化。因为他初始化的时候是需要将数据全部读取出来放到内存当中。 优化:
1:可以尽可能减少sp文件数量(IO需要时间);
2.像这样的初始化最好放到线程里面;
3.大的数据缓存到数据库里面。
解决:
将SplashActivity和MainActivity合为一个。 一进来还是现实的MainActivity,SplashActivity可以变成一个SplashFragment,然后放一个FrameLayout作为根布局直接现实SplashFragment界面。SplashFragment里面非常之简单,就是现实一个图片,启动非常快。当SplashFragment显示完毕后再将它remove。同时在splash的2S的友好时间内进行网络数据缓存。这个时候我们才看到MainActivity,就不必再去等待网络数据返回了。 问题:SplashView和ContentView加载放到一起来做了 ,这可能会影响应用的启动时间。 解决:可以使用ViewStub延迟加载MainActivity当中的View来达到减轻这个影响。 viewStub的设计就是为了防止MainActivity的启动加载资源太耗时了。延迟进行加载,不影响启动,用户友好。但是viewStub加载也需要时间。等到主界面出来以后。viewStub.inflate(xxxx);如何设计延迟加载DelayLoad
第一时间想到的就是在onCreate里面调用handler.postDelayed()方法。 问题:这个延迟时间如何控制? 不同的机器启动速度不一样。这个时间如何控制? 假设,需要在splash做一个动画--2S 需要达到的效果:应用已经启动并加载完成,界面已经显示出来了,然后我们再去做其他的事情。 如果我们这样: mHandler.postDelayed(new Runnable() { @Override public void run() { mProgressBar.setVisibility(View.GONE); iv.setVisibility(View.VISIBLE); } }, 2500);复制代码
是没法做到等应用已经启动并加载完成,界面已经显示出来了,然后我们再去做其他的事情。
问题:什么时候应用已经启动并加载完成,界面已经显示出来了。onResume执行完了之后才显示完毕。不行。
可以用以下二个方法:
-
1.onwindowfocuschange
-
2.ViewTreeObserver
具体的代码出下:
public class MainActivity2 extends FragmentActivity {private Handler mHandler = new Handler();private ViewStub viewStub;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); final SplashFragment splashFragment = new SplashFragment(); final FragmentTransaction transaction = getSupportFragmentManager() .beginTransaction(); transaction.replace(R.id.frame, splashFragment); transaction.commit(); viewStub = (ViewStub) findViewById(R.id.content_viewstub); // 1.等整个界面加载完毕后,立马再加载真正的布局进来 getWindow().getDecorView().post(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { viewStub.inflate(); } }); } }); // 2.延迟一段时间做动画,然后把splashfragment移除 getWindow().getDecorView().post(new Runnable() { @Override public void run() { mHandler.postDelayed(new DelayRunnable(MainActivity2.this, splashFragment), 2500); } }); //3.同时异步预加载数据。 //....}static class DelayRunnable implements Runnable { private WeakReference<Context> contextRef; private WeakReference<SplashFragment> fragmentRef; public DelayRunnable(Context context, SplashFragment splashFragment) { contextRef = new WeakReference<Context>(context); fragmentRef = new WeakReference<SplashFragment>(splashFragment); } @Override public void run() { FragmentActivity context = (FragmentActivity) contextRef.get(); if (context != null) { SplashFragment splashFragment = fragmentRef.get(); if (splashFragment == null) return; final FragmentTransaction transaction = context .getSupportFragmentManager().beginTransaction(); transaction.remove(splashFragment); transaction.commit(); } }}@Overrideprotected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null);}}复制代码
xml布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.applicationstartoptimizedemo.MainActivity" ><ViewStub android:id="@+id/content_viewstub" android:layout="@layout/main_viewstub"//这个就是MainActivity的布局 android:layout_width="match_parent" android:layout_height="match_parent"/><FrameLayout android:id="@+id/frame" android:layout_width="match_parent" android:layout_height="match_parent" ></FrameLayout></RelativeLayout>复制代码
以上就是我对应用启动的优化,通过优化,明显感觉APP启动快很多.