首页 > 代码库 > android加载速度优化,通过项目的优化过程分析
android加载速度优化,通过项目的优化过程分析
通过这么长时间的盒子开发以及之前手机项目的经验,总体感觉两种不同设备还是有很多不同的地方的,首先一点不同的就是,手机项目和电视项目默认启动页面加载速度有重要区别
对于手机:手机加载网络数据,由于屏幕小,如果主页有网络图片的情况下,基本都是显示默认图片,这也是由于网速的限制,更重要的是手机上基本是图文混排,用户没看到图片可能焦点就在文本上了。
对于电视:如果应用首页加载使用默认图,会感觉特别丑,因为屏幕大,重要信息都是图片,如果没有图片,那用户看到的都是空白,用户的焦点没有了,只有等待和抱怨。
因此,电视首页加载至为重要。
2.0针对以上情况,是增加了一张纯黑的View盖住下边的View,等待首页轮播图大图数据加载完成让其消失,但是黑屏时间过长。以下就是针对黑屏时间已经做的和建议的优化
通过之前手机项目列表优化、ViewPager优化以及其他项目优化的经验,对2.0做了一些优化,主要有以下几点:
1、如果本地没有网络图片的硬盘缓存,图片下载应该在Application启动过程中就已经开始,比如蓝光,是在Launcher启动后,发广播给蓝光,蓝光开始预加载。(针对2.0没有这么做,可以做技术调研是否可行,只是一种建议)
2、如果本地有网络图片缓存,首先将图片从硬盘缓存加入到内存缓存,原因对于电视来说,毫秒必争啊,相对来说从File到Bitmap/Drawable等图片对象的decode过程相当慢,小图片几百ms,大图要1s到Ns,所以,这一步至关重要啊。
如果不是首次启动Launcher,首页图片已经是加载在本地了,UniversalImageLoader 这个框架加载图片过程是首先从内存中获取图片,获取不到decode本地图片,本地没有再从网络获取,和咱们基本思路一致,decode过程很慢,因此在Application中首先把图片异步加载到内存中,当然不一定能加载完,如果加载不完,等到执行到UniversalImageLoader加载图片的时候,它再去decode本地图片,decode的图片数量会减少,至少会省下一部分时间。
下图为没有优化前,看最后一条是首屏大图加载完成时间,大图加载完的耗时时间:
以下是优化后,加载完成的时间:
总耗时从13840ms减少到3459ms
3、通过第二步的优化,本以为减少这么长时间,总过减少了至少10秒,速度会快很多,但表现并没有多好,继续分析
首先查找图片下载前后的操作
发现最原始的是在HomeActivity的onCreate中调用了matrixPager.loadData(false);这行函数,这个函数里边调用了strategy.loadPageData(isRefleshData);方法
loadPageData()是个模板方法,strategy是用了策略模式,对应的类有FivePageViewStrategy和FourPageViewStrategy两个类实现了loadPageData(),我的盒子上是五个页面,因此是使用了FivePageViewStrategy这个策略,debug发现,实际上图片下载是先下载了应用的图片数据,这样其实不合理,应该首先下载最先看到的页面,也就是推荐页的数据
loadPageData使用的是线程池,并没有规定线程执行顺序,我把它改成了AsyncTask,虽然也是线程池,但里边通过Deque实现了线程的队列下载,先进先出,让推荐页(默认启动页)首先加载数据,因为只有他加载完数据后,才会让黑屏消失。
4、以上一步搞好后,对HomeActivity的onCreate进行了分析,Log发现,onCreate从开始到执行到loadData,期间耗时5s多,看下图onCreate before loadData,这句是在loadData执行之前的Log,从onCreate开始到执行loadData,阻塞了5秒,这个时间还是很长的。
通过跟踪代码,发现其实是在setContentView中加载布局文件耗时,逐步跟进,首先发现了GuidePageView这个类,2.0项目中首次安装会有引导页,引导页其实只是viewpager放在了HomeActivity(Launcher Activity),这几张引导页的图片也是从本地图片decode的,decode过程非常缓慢,这里就没有使用上一种方式,而是将图片decode过程放在子线程中,decode完成后,再给ViewPager设置图片。
如上图:优化前Log:loadImage GuidePageView::init耗时2751,优化后如下图耗时从 2751ms到28ms:
onCreate before loadData耗时也减少了很多。
这里边其实隐藏了一个重要知识点,就是不要再View的两个签名的构造方法中做太多事情,因为布局文件在inflate过程中会调用这个构造方法,逻辑阻塞时间越长,inflate时间越长
5、在initView中,有一步发送统计数据的操作,这一步操作调用了我封装的Device类的方法,比如getSN,getMac等方法
这些都是读的本地文件,也是耗时,这段拿到了子线程,log就不附了,总共节省1s左右时间
6、在initView中,还有一步操作很耗时,但是我也无能为力了,因为对2.0代码了解的不多,参与的也少,所以只能给建议了,里边涉及的东西还是要说一下。
initView中有以下操作
if (versionCode == 1) {
FourPageViewStrategy forstrategy = new FourPageViewStrategy(matrixPager);
matrixPager.initTabViewByIndex(2, forstrategy);
} else if (versionCode == 2) {
FivePageViewStrategy fivstrategy = new FivePageViewStrategy(matrixPager);
matrixPager.initTabViewByIndex(2, fivstrategy);
}
这是判断首页是五个页面还是四个页面的策略选择方式
在构造函数中有如下代码:
view3dPager.addTabPageView(new MatrixAppView(view3dPager.getContext(), new AppFocusDirectioner()), 0);
view3dPager.addTabPageView(new MatrixMovieView(view3dPager.getContext(), new MovieFocusDirectioner()), 1);
view3dPager.addTabPageView(new MatrixRecommendView(view3dPager.getContext(), new RecommendFocusDirectioner()), 2);
view3dPager.addTabPageView(new MatrixTVView(view3dPager.getContext(), new TvFocusDirectioner()), 3);
view3dPager.addTabPageView(new MatrixGameView(view3dPager.getContext(), new GameFocusDirectioner()), 4);
重要的还不是这里,是addTabPageView中的五个new操作,看其中一个吧
public MatrixAppView(Context context, RecommendViewFocusDirectionListener listener) {
super(context, listener);
init();
}
protected void init() {
// code here....
View view = inflate(getContext(), R.layout.matrix_app_view, null);
this.addView(view, params);
// code here...
}
粗略看了下这个inflate的layout,没有自定义View,但耗时每个二三百、三四百毫秒,五个加起来,能达到两三秒,inflate过程很长,如果这几个View能在加载HomeActivity之前实例化完成或者实例化几个,类似图片加入memory过程中,可能时间会缩短
总结下:
1、除了针对View的操作,能拿到子线程就拿到子线程去做操作;
2、加载顺序很重要,结合队列让线程池里的线程执行有序,保证最先在用户面前的界面的数据最先加载,以最快显示出来;
3、图片decode过程很漫长,如果是首屏,很有必要在Application中将图片首先加载到memory,因为Application是整个应用首先加载的类;
4、不要在自定义View的构造方法中做太多耗时操作,这样更会延长inflate过程;
5、inflate过程很漫长,如果自定义View中inflate了其他布局,有必要放在子线程执行inflate操作(首先确保inflate的布局中没有其他View在构造方法中做了对View的操作,否则会报错)。
以上在以后的项目中可以借鉴,如果有其他建议或疑问,欢迎共同讨论
纯手打,如有不同见解或错误理解,欢迎指正
android加载速度优化,通过项目的优化过程分析