当前位置: 首页 > 科技观察

SwipeRefreshLayout引发的一场屠杀

时间:2023-03-20 12:10:07 科技观察

关于下拉刷新,不管是普通用户还是开发者都再熟悉不过了。过去,下拉刷新和开源控件的设计都非常流行。党中央(谷歌),所以党中央在支持包里加了一个下拉刷新控件——SwipeRefreshLayout。没有陌生人。至于如何使用SwipeRefreshLayout,我就不说了。如果你不知道,你会自觉地撞墙。Swiperefreshlayout.gif作为MaterialDesign的坚定支持者,我在实战中也大刀阔斧地使用了SwipeRefreshLayout。我只是简单地写了一个BaseSwipeRefreshLayout类来初始化一些基本属性。因为好用,所以没有做太多的封装。如果需要使用下拉刷新,直接在布局中引用这个Base类即可,没想到,却埋下了一个坑,引发了血案。当然,并不是说Base类本身的代码有什么错误,而是因为一些奇妙的组合导致了一些血淋淋的bug,我继续下八篇。在实际开发中,基本上Activity和Fragment都用到了这种下拉刷新。我的主页是一个经典的设计,一个Activity通过ViewPager维护4个Fragments。第一页和第二页有用到下拉刷新。Fragment使用延迟加载。关于懒加载,可以看我的另一篇文章。ViewPager+FragmentLazyLoad***解决方案。这段代码绝对没有问题,一切看起来都那么美好!庆幸,就是这么自信。然而有一天突然发现一个无法解释的现象:当我启动app一直停留在***页面时,即使静止不动,cpu使用率仍然很高,而且呈线性,几乎没有波动.切到第二页,加载数据后,cpu使用率立马下降。我当时呵呵了。所以我开始检查和优化。经过简单的分析,基本可以确定问题出在第二页。不幸的是,我的第二个页面布局很复杂。最上面是一个很大的自动轮播,然后是两个水平的RecyclerView,***还有一个垂直的RecyclerView,当然中间嵌套了一些小view。我们再分析一下问题:进入App,停在首页。因为延迟加载,第二个页面的View已经初始化了,但是还没有loadData。这个时候CPU使用率很高。然后切换到第二页完成loadData,CPU使用率立马恢复正常。而如果我不使用懒加载来加载Fragment,就不会出现这个问题。首先我怀疑是不是我的懒加载有什么问题。调试后发现一切正常,也没发现什么不合理的地方。地方。以老司机的经验来看,既然cpu一直很高,很有可能是某个视图一直在测算。那么这里怀疑是RecyclerView,但是我这里有3个RecyclerView,只能通过剔除法一个一个写出来,然后观察cpu情况。然而,奇怪的是,即使我都写出来了,也没有用,那么看来问题不是View重复测量导致的。这时,矛头对准了上方的大转盘。轮播可以自动滚动,永远循环。可能是控制不合理,或者定时器的使用有问题。好像看到了曙光,问题应该出在这里,于是把轮播注释掉,再看。我去了,还是没用。问题似乎陷入僵局。按照剧情的正常发展,这时候应该下楼去点根烟,边抽边和同事交流,然后深吸一口气,吐出淡蓝色的烟雾,看着蓝色的烟雾缓缓升起。突然喊道:我知道了。然后他啪的一声掐掉烟头冲上楼,留下同事们在烟雾中狼狈不堪。但事情是这样的:我不抽烟。由于此时不能根据主观经验快速定位问题,只能采用笨办法。它仍然是一种排除方法。我把可疑的代码一行一行注释掉了。到最后,我几乎把整个班级都评论完了。debug进来后,并没有代码执行,而是加载了一个layout。不过,我不想再说了。查了JAVA代码,还是没有定位到问题所在。老司机已经有些不安了。这是布局问题吗?透支?没想到黑客发现,罪魁祸首竟然是我上面写的BaseSwipeRefreshLayout引起的。自己挖了一个坑,把自己埋进去就得填上。publicclassBaseSwipeRefreshLayoutextendsSwipeRefreshLayout{publicBaseSwipeRefreshLayout(Contextcontext){super(context);init();}publicBaseSwipeRefreshLayout(Contextcontext,AttributeSetattrs){super(context,attrs);init();}privatevoidinit(){this.getContext(),-50),DensityUtil.dip2px(getContext(),30));this.setColorSchemeColors(getContext().getResources().getColor(R.color.primary_green));setRefreshing(true);}@OverridepublicbooleanonStartNestedScroll(Viewchild,Viewtarget,intnestedScrollAxes){return!isRefreshing()&&super.onStartNestedScroll(child,target,nestedScrollAxes);}}上面的类有几行,只是做了一些统一的初始化操作,但是导致了一些问题。你能在三分钟内看到问题吗?问题出在setRefreshing(true);init()方法中的语句。这是我写的时候的想法。一进入界面就会load,所以我干脆在Base里面加上loading。这样就不用每个接口都写一遍了。但是由于我在上面的场景中使用了懒加载,所以问题就来了:虽然我停留在第一页,但是第二页的View已经初始化好了,自然而然SwipeRefreshLayout的加载圈已经在不停的转了,所以cpu开始保持非常线性的高。切换到第二页,数据加载完成后setRefreshing(false),加载圆圈消失,cpu恢复正常。费了一番功夫,还好***还是把坑填上了。还有一个意想不到的地方就是原来的SwipeRefreshLayout不是很省油。实际开发过程中的难点不是如何解决问题,而是如何排查和定位问题。大多数情况下,我们可以凭借自己的积累和经验快速定位到问题所在。但是这次自己挖的坑真的很隐蔽,从JAVA代码到Xml几乎一行一行的检查,费了我很大的功夫,终于把坑填上了。在开发中还是要考虑的更全面,少挖坑。有坑就有套路可以找。仔细分析问题,从JAVA代码到xml一步步排查,就会豁然开朗。