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。
关于应用DelayLoad延迟加载的几种方式
1、View.postDelayed();
2、Handler.postDelayed();
3、getWindow().getDecorView().post(){Handler.postDelay()}
而前两种方式虽然都有Delay效果,但并不是真正Delay了我们设置的时间,而第三种方式才是正确的Delay了我们想要的时间,原因如下:
关于Activity界面的启动,首先是在onCreate()方法中会对contentView、DecorView和ActionBar等进行初始化,比如:contentView进行inflate,我们便可以在这个方法中通过findViewById来找到对应的控件了,但是此时我们只是把那些对应的控件创建了一个对象而已,它们并没有add在界面上。而真正把contentView 添加到界面上的操作是在OnResume()方法执行时候,包括ViewRootImpl的初始化。而contentView的绘制会在onResume()方法执行完后的二次performTraversals()方法进行绘制。
所以要想达到DelayLoad懒加载效果,是在所有的View绘制完成后进行Delay效果,而上面的第一第二种方式都是在第一次performTraversals()后执行,该次只是为第二次调用performTraversals()方法做一些准备工作,这样就达不到准确的Delay效果了,因为第二次的performTraversals()就是真正的进行View的测量布局和绘制了,这肯定是需要时间的,所以第一和第二种方式Delay的时间是需要再减去这个View绘制的时间的。
如果是想在界面刚绘制完成后做一些事情,或者有些事情必须在UI绘制完成显示后做的话,那可以通过这个方法:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
}
});
如果想Delay的话,可以这样:
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//do something
}
},3000);
}
});
Android性能优化之Splash页应该这样设计的更多相关文章
- 【腾讯Bugly干货分享】Android性能优化典范——第6季
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/580d91208d80e49771f0a07c 导语 这里是Android性能优 ...
- Android性能优化问题总结
性能优化这块,分为UI性能优化.内存优化.数据库优化.网络优化.耗电优化等等.可以从1.如何发现问题,2.怎么解决问题,3.解决效果对比,这几个方面去描述.举个简单例子——UI优化,可以从 UI出现什 ...
- Android性能优化系列之App启动优化
Android性能优化系列之布局优化 Android性能优化系列之内存优化 Android性能优化系列之apk瘦身 应用的启动速度缓慢是我们在开发过程中常常会遇到的问题,比方启动缓慢导致的黑屏.白屏问 ...
- Android性能优化系列之apk瘦身
Android性能优化系列之布局优化 Android性能优化系列之内存优化 为什么APK要瘦身.APK越大,在下载安装过程中.他们耗费的流量会越多,安装等待时间也会越长:对于产品本身,意味着下载转化率 ...
- Android性能优化的方方面面
通常项目比较大的APP都面临着如下性能问题,APP启动慢.界面跳转慢.事件相应慢.滑动和动画卡顿.展现内容慢等,有的公司处理的很好,有的还有很大的优化空间,对于性能优化,怎么去做,我总结了一下性能优化 ...
- [Android 性能优化系列]内存之基础篇--Android怎样管理内存
大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...
- android 性能优化
本章介绍android高级开发中,对于性能方面的处理.主要包括电量,视图,内存三个性能方面的知识点. 1.视图性能 (1)Overdraw简介 Overdraw就是过度绘制,是指在一帧的时间内(16. ...
- Android性能优化典范第二季
Google前几天刚发布了Android性能优化典范第2季的课程,一共20个短视频,包括的内容大致有:电量优化,网络优化,Wear上如何做优化,使用对象池来提高效率,LRU Cache,Bitma ...
- Android性能优化典范第一季
2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...
随机推荐
- 使用json-Server与postman快速模拟服务环境搭建
在前后端分离的这种工作模式下,分工明确,各司其职.前端负责展示数据,后端提供数据.然而,在这种过程中对于接口的规范 需要提前制定好.例如根据规范提前模拟数据,这个时候就比较麻烦的.JsonServer ...
- Centos下出现read-only file system 的解决办法
Centos下出现这种情况说明磁盘只能读不能写,出现这种情况一般是因为不正常的关机或者硬盘损坏导致磁盘挂载出现问题. 本萌新也遇到了这个问题,尝试了各种命令都不行,最后用了mount -o remou ...
- python安装pip以及导入第三方包
python有着强大的第三方库,数量很多且功能强大. 最原始的办法是在官网上下载压缩包,解压,然后运行setup.py来进行安装. 显然这种方法很繁琐,不方便.因此有了包管理工具. pip是一个包管理 ...
- ⏰Day.js 2kB超轻量时间库 和Moment.js一样的API
Moment.js 是一个大而全的 JS 时间库,很大地方便了我们处理日期和时间.但是 Moment.js太重了(200k+ with locals),可能一般项目也只使用到了她几个常用的API.虽然 ...
- JSTL标签四种判断语句的用法
一.条件运算符 ${user.gender==1?'男':'女'} 二.if() <c:if test="${2>1}">code..</c:if> ...
- Redis持久化的两种方式(RDB和AOF)
redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File). RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储 ...
- tree的遍历--广度优先遍历
一.二叉树demo var tree = { value: '一', left: { value: '二', left: { value: '四', right: { value: '六' } } } ...
- Go 处理yaml类型的配置文件
先说一下,这里用到了很多关于反射类型的功能,可能刚开始看代码,如果对反射不熟悉的可能会不是非常清晰,但是同时也是为了更好的理解golang中的反射,同时如果后面想在代码中可以直接从我的git地址get ...
- Python学习---字符串处理
This world is but a canvas to our imagination. 世界只是我们的想象的画布. ----Apri 22 ''' 题目内容: "Pig Latin&q ...
- 2017-暑假作业-Java语言程序设计
任务列表 1.学会使用Markdown做笔记 本篇随笔就是使用的Markdown语法.养成做笔记的习惯! 参考资料: 极简MarkDown排版介绍(How to) stackedit:在线Markdo ...