最近做一个币行情显示的功能,介面已有,重点就是搞懂栏位,然后进行显示。另外还得定时刷新(每隔10s请求刷新一次),同时如果刷新过程价格有涨幅需要分别以不同的颜色(红色表示涨,绿色表示下降)进行渐变显示。
简单说下颜色变化:由于小萌新尝试过程本来是想每个item进行颜色动画动画或者说定时进行颜色的改变,但是发现实际过程模拟器出现了线程溢出问题,初步尝试发现每个item进行颜色渐变的动画,不如间隔的进行整体刷新,刷新过程中利用新数据与当前数据进行对比,进行颜色变化。另外,如果当前已经是红色或者绿色,下次刷新间隔缩短到3s进行颜色默认恢复,然后再恢复到10s进行渐变处理,达到整体效果。 可能说起来模糊,直接看效果:
定时刷新采用 handler.postDelayed(runnable, 10000); 实现。为了处理界面不可见时,停止定时器的触发,则需要考虑再多个地方进行 handler.removeCallbacks(runnable);的调用处理。我想你不希望界面不可见或者销毁了,定时器还在运行的状态吧,那样或许还会导致泄漏,崩溃等问题!!!
1. 不自觉的想到需要在 onDestroyView 中进行释放
结构:HomeActivity->Fragment(多碎片切换利用Hide/Show的方式)->Viewpaper+Fragment(设置了infosViewPaper.setOffscreenPageLimit(3);)的方式
问题来了:
1. 主Fragment利用Hide/Show的方式切换时,只会触发主碎片的onHiddenChanged的周期回调,但是其子碎片(ViewPaper+Fragment)的onHiddenChanged并不会触发,其他生命周期也不会被触发 - 因为子碎片并没有被重建(setOffscreenPageLimit(3),至少容纳三个碎片) - 也就是说底部导航切换的情况下,子碎片(比如市值界面)的生命周期情况并不知晓!!
2. 自选和市值子碎片切换的时候(ViewPaper切换方式), 只会触发setUserVisibleHint周期回调
3. 当点击市值中条目进行页面跳转时,触发onPause->onStop->(切换回来时触发)onResume
So,针对2、3 只需要在onResume(需要注意第一次启动也会触发的问题)、setUserVisibleHint中处理下定时器的开启关闭即可
@Override public void onResume() { super.onResume(); if (!bFirstStart){ ///< 定时请求列表并做数据变化对比,然后做刷新动画处理 handler.postDelayed(runnable, 10000); } bFirstStart = false; Log.i(TAG, mParam1 + "onResume"); }
@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (!isVisibleToUser) { handler.removeCallbacks(runnable); }else{ ///< 定时请求列表并做数据变化对比,然后做刷新动画处理 handler.postDelayed(runnable, 10000); } Log.i(TAG, mParam1 + "setUserVisibleHint isVisibleToUser="+isVisibleToUser); }
针对1,由于子碎片没有触发,而主碎片切换的情况下会回调onHiddenChanged方法,这个时候就可以在主碎片的onHiddenChanged中调用子碎片的onHiddenChanged方法,主动进行回调:
主碎片处理:
@Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); ///< 由于父Fragemnt没有重新创建View,因此只能通过该方式主动调用子碎片的生命周期 if (null != getCurrentFragment()){ getCurrentFragment().onHiddenChanged(hidden); } Log.i(TAG, mParam1 + "onHiddenChanged"); }
其中获取当前碎片的方法 getCurrentFragment:
/** * 获取当前的Fragment * @return */ private Fragment getCurrentFragment() { if (null == infosViewPaper){ return null; } FragmentManager manager = getChildFragmentManager(); final int currentItem = infosViewPaper.getCurrentItem(); Fragment fragment = manager.findFragmentByTag(makeFragmentName(infosViewPaper.getId(), currentItem)); return fragment; }
然后子碎片同样处理下:
@Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if (hidden){ handler.removeCallbacks(runnable); }else{ ///< 定时请求列表并做数据变化对比,然后做刷新动画处理 handler.postDelayed(runnable, 10000); } Log.i(TAG, mParam1 + "onHiddenChanged hidden=" + hidden); }
这样就完成了所有可见不可见的情况的处理。有时候发现还是蛮烦的,当然如果对碎片的生命周期了解很熟的话,应该就不会那么烦了!!!
还有就是额外补充下(关于通用适配器封装相关):关于通用BaseAdapter资源释放的问题,需要主动调用Adapter的onDetachedFromRecyclerView方法
@Override public void onDetach() { super.onDetach(); ///< 适配器资源释放 if (null != marketValueAdapter && null != xrerecycleView) { marketValueAdapter.onDetachedFromRecyclerView(xrerecycleView); } Log.i(TAG, mParam1 + "onDetach"); }
然后,通用适配器就可以做具体释放处理
BaseAdapter.java
private WeakReference<Context> contextWeakReference = null;
/** * 适配器资源释放 * @param recyclerView */ @Override public void onDetachedFromRecyclerView(RecyclerView recyclerView) { super.onDetachedFromRecyclerView(recyclerView); if (null != contextWeakReference){ contextWeakReference.clear(); contextWeakReference = null; } }
差不多这个定时就可以了。也避免了一些泄漏崩溃啥的,同时还有就是关于rxjava不停同时请求导致内存溢出的问题 - 尽量逻辑上有个顺序:
1. 比如说当前请求是首次请求,等首次请求结束后再开启定时刷新功能;
2. 手动刷新过程首先停止定时刷新,避免数据混淆和干扰导致界面等问题(可以理解为多线程的安全),等手动刷新结束后再开启定时刷新
3. 由于防止定时多次刷新问题,再下一次进行定时刷新时清空/释放上次刷新请求,然后进行新的刷新
经过逻辑的稍微更严谨处理以及相关资源处理,目前还是能经得住测试的。当然小萌新还得加强各方面的深入(比如rx深入),另外关于自己封装的BaseAdapter通用适配器以及通用请求MonkeyLei:Android-基本的MVP结构的模板工程(泛型,Rx通用请求,BaseAdapter实践目录链接)还得再考虑验证和完善......