Android性能优化之Splash页应该这样设计
目前SplashActivity的设计
目前市场上的应用在启动时基本上都会先启动一个SplashActivity,作为一个欢迎界面,为什么这样设计呢?个人总结有三个优点:
1、可以给用户更好的体验
比如:可以由后台动态的改变欢迎的图片,或者显欢迎xxx回来,新浪微博的就是这种交互。
2、可以缩减App的启动时间
由上一篇博文中知道app启动的耗时主要是在Application初始化中和MainActivity的界面绘制前,由于MainActivity的业务和布局复杂度肯定比只显示一张图片的界面高,所以,加入一个显示一张图片的Splash页可以优化应用的启动。
3、可以在应用启动时做更多的事
一般来说SplashActivity一般会设计成停留2到4s不等,或者根据数据的加载程度来动态的设置Splash界面的停留时间,既然停留那么久,那么当然可以在这个界面背后做一些事以备MainActivity的快速显示,比如:数据的预加载、sp的初始化、网络请求等。
当然你可能有些疑问,那这样初始化放在Application中也可以啊?也用异步操作数据也是一样啊?
答案是不一样!正如上篇所说的,Application初始化时并不会加载界面,而是在它创建完和初始化完成后,开始创建Activity时才开始绘制Theme中的background和绘制布局,所以用一个轻量的Splash页给它设置一张背景欢迎图,这样就立马能显示界面了,而在这个界面中还可以做其它的初始化操作,这样在视觉上即达到了app的快速启动,又添加了体验和做数据的初始化。
相反如果过多的放在Application中,则在点击app图标启动时会感觉延迟,必须要把Application中的东西都做完才进入Activity的配置和绘制中。
目前大多数应用的Splash页设计的不足之处
目前大多应用的Splash页设计都是利用一个Activity,取名叫SplashActivity,然后在这个SplashActivity中加入一个背景图,然后再new Handler().postDelayed()几秒中,再startActivity跳入主界面,这样设计看起来非常不错,既可以在SplashActivity初始化、预加载数据,还可以提高应用的启动速度。
不过这确实提高了应用的启动速度,毕竟我们比较快的看到了第一帧——SplashActivity,不过在SplashActivity之后,还需要调到MainActivity啊,虽然MainActivity中的一些数据可以在SplashActivity做预取,不过这中间需要有Intent的传递过程,而且MainActivity中布局还没加载进来,所以还是需要再加载和绘制布局界面,然后才能填入数据,所以这样看来,在跳转到MainActivity中,还是需要做界面的绘制和数据的加载(包括Intent的数据传递)。
以往的SplashActivity的设计图
这样看来上面这个设计流程可以这样表示:
性能优且体验棒的Splash页的设计
从上面这个设计图来看,其中有些操作能不能去除呢?既能达到app启动速度的提高,也能对数据的预加载还能减去Splash和MainActivity之间不必要的数据传递和View的分开绘制。
答案是能的,既然SplashActivity和MainActivity分开进行操作还是不完美,那么可以考虑把它们合为一起,即:一开始还是显示MainActivity,SplashActivity变为SplashFragment,然后放一个FrameLayout作为根布局去显示SplashFragment界面,这样在SplashFragment显示时候利用显示的2~4s间的空隙时间做网络请求去加载数据,这样待SplashFragment显示完后再remove,这样将看到的是有内容的MainActivity,就不必再去等待网络请求去返回数据了。当然,这种方式是把load Splash View和ContentView合二为一了一起加载,这可能会影响应用的启动时间,这时我们可以用ViewStub延迟加载MainActivity中某些View从而减去这个影响。
如下设计:
优化前后效果对比
这里为了测试,我把Splash页的delay时间都设为2.5s。
优化前:
优化后:
优化后其实是把SplashActivity用Fragment显示,显示完后再remove,这样在显示的时候,MainActivity中还可以直接加载网络数据,这样在显示完SplashFragment后则直接显示主页了,而省去了ProgressBar进度条的网络加载过程。
代码:
private Handler mHandler = new Handler(); //... final SplashFragment splashFragment = new SplashFragment(); final FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.frame, splashFragment); transaction.commit(); //... mHandler.postDelayed(new DelayRunnable(this, splashFragment, mProgressBar), 2500); //... static class DelayRunnable implements Runnable { private WeakReference<Context> contextRef; private WeakReference<SplashFragment> fragmentRef; private WeakReference<ProgressBar> progressBarRef; public DelayRunnable(Context context, SplashFragment splashFragment, ProgressBar progressBar) { contextRef = new WeakReference<Context>(context); fragmentRef = new WeakReference<SplashFragment>(splashFragment); progressBarRef = new WeakReference<ProgressBar>(progressBar); } @Override public void run() { ProgressBar progressBar = progressBarRef.get(); if (progressBar != null) progressBar.setVisibility(View.GONE); Activity context = (Activity) contextRef.get(); if (context != null) { SplashFragment splashFragment = fragmentRef.get(); if (splashFragment == null) return; final FragmentTransaction transaction = context.getFragmentManager().beginTransaction(); transaction.remove(splashFragment); transaction.commit(); } } } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); }
其中FrameLayout作为MainActivity的根布局用作SplashFragment的全屏显示。为了更优则可以考虑ViewStub,在SplashFragment显示时再进行加载额外的View。
关于耦合性,其实很低,Splash页面有专门一个SplashFragment去配置,而MainActivity只是控制它的加载与remove。
</div>来自: http://blog.csdn.net/u010687392/article/details/50525697