最近做一个币行情显示的功能,介面已有,重点就是搞懂栏位,然后进行显示。另外还得定时刷新(每隔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实践目录链接)还得再考虑验证和完善......


推荐阅读:
相关文章