UI优化记录
问题发生
实习公司的项目中,负责添加注册登录模块(原本是调用网页来实现交互的)的ui及其适配、代码编写。但是按照设计图做好ui后,点击“登录按钮”进入登录注册模块,相当耗时(2-3s),而且切换页面进行跳转的动作,也比较耗时。反复操作后,应用崩溃。
优化目标
- 减少页面初始化时间
- 流畅进行跳转。
原本的页面跳转策略+伪代码
- 在负责该模块的activity中,使用Handler来发送消息,在Handler的处理消息方法中进行跳转。
利用Stack来存储View,新页面先初始化,再调用一些显示前需要的调用方法,显示view,压栈。
View view = inflate(R.layout.id,null); view.doSomethimgBeforeShow(); setContentView(view); viewStack.push(view); view.doSomethingAfterShow();
回退
View view = viewStack.pop(); view.doSomethingBeforeDestroy(); view = null; if(viewStack.size()>0) setContentView(viewStack.peek()); else close();
寻找问题
- 一开始猜想是图片方面导致的内存不够,所以也做了相应的处理来回收内存,见我的上一篇博客。
- 做了图片方面的处理后,速度稍微快了一些,但上述现象仍然很严重。于是打算使用TraceView来看看到底是什么方法,如此的耗时…
TraceView分析(真机上)
在UI线程中,注意用红色框框起来和箭头所指的方法,大量的时间都被消息分发、消息处理所占用。将近两秒!我当时也有点纳闷…又没有耗时操作,只是个单纯的切换显示view,为什么用Handler呢?所以,第一个需要优化的就是避免使用Handler来进行页面跳转
模拟器上的测试也能看出来,但是有时候可能没有真机上这么明显。当cpu比较繁忙的时候,或者当前MessageQueue中消息队列很长的时候,发出一个message放到MessageQueue中可能需要很长时间才会被Looper轮询到,增加了响应时间的不稳定性。
去掉Handler来直接进行跳转后,继续使用TraceView来分析。
可以看出,耗时减少了(1.5s,也没有减少多少),但是现象仍然很明显。前面的一些方法都是系统方法,挨个往下查看,注意到最后一条,是我们控制注册登录模块的activity,它的onCreate方法占用了比较多的时间,点开继续看。
acitvity中的一个a方法(工程被混淆过)占用了onCreate()方法的99%的时间,点开继续看。
仍然是这个a,没看出什么,继续点开这个标号17。
这次有东西了,LoginAndroid这个类就是点击登录后第一次加载的页面,可以看到init()和inflate方法占据了绝大多数时间,点开init()方法看。
customEditView是一个自定义控件,看耗时比例,控件的init()方法和TypeFace这个占据绝大多数时间,先不管TypeFace,点开init()。
哎~又出现了这个TypeFace类,打开代码,发现这个类在模块中是用来获取字体资源问题,设置字体的。第二耗时的是输入框的初始化。
上面仅仅是第一个页面的初始化步骤中耗时的部分,其他页面也是一样的情况。那么问题很有可能就在这里。
将字体设置去掉后,再次在真机上调试,效果有了极大的提升,腰不酸腿不疼了。
继续
找到了问题的原因是加载字体文件过于耗时,但是字体要求又是ui上标明了的,有没有什么办法可以使用字体也能达到性能上的要求呢?
上网寻找相关资料,发现TypeFace这个类对象可以被多个对象多次使用,那这样的话,只需要初始化一次TypeFace就可以了,之后只使用这一个对象就能设置字体。所以,在全局范围内只实例化一个TypeFace即可。
最终的ui优化方案
- 避免使用Handler来进行页面跳转。
- 图片资源及时回收。
- 一些资源文件(例如这次的TypeFace),如果可以重复使用,那么实例化一个对象,多次调用即可。
还有一点文中没有提到,就是可以把一些在页面中不需要伴随view初始化的操作,放到其他方法中,在view初始化,显示出来后,再调用执行,就是之前伪代码中提到的
view.doSomethingAfterShow();
这样页面已经显示出来了,再有一些耗时的操作,不至于会导致页面进入的时候耗时过长。
结论
- TraceView是神器。
- 很多问题在模拟器上体现不出来,真机的情况要更加复杂多变,所以这些问题也大多出现在真机测试中。只有多遇到问题+多解决问题+多总结,才能在编码过程中先见性的预防可能发生的性能问题。