1.前言

在App的开发中,列表,流式布局是最常用的UI元素。通常RecyclerView的ViewHolder会根据业务的需要,异步处理一些耗时操作,再根据处理后的结果进行UI的更新。

这种情况下,有可能出现问题:由于RecyclerView针对ViewHolder有回收复用机制,所以当数据回来后,如果这个ViewHolder已经被复用则可能导致数据更新错误。

通常我们会通过打TAG或判断唯一标识来确保数据更新的准确性。为此我们开始考虑,有没有更好的处理办法呢?

每一个ViewHolder不需要进行其他处理,即可保证与异步数据是对应关系,不会导致复用错误。另外通过使用MVVM模式对View和数据层进行解耦。

MVVM模式在Android中的使用已经非常广泛了,V层(Activity或Fragment)与VM层(ViewModel)通过LiveData来进行数据交换。其中V层实现了LifecycleOwner接口从而持有了生命周期(LifeCycle对象),并观察VM层的LiveData的变化。

LiveData在接收到M层的数据变化后根据LifecycleOwner当前所处的生命周期,来决定是否通知给Observer,即V层去更新UI。

因此我们想到ViewHolder能不能像Activity或Fragment一样,根据自己的生命周期变化,来处理VM层返回的数据呢?

在这个思维模式的前提下,我们开始考虑V层的拓展。通常我们在处理业务逻辑时可以认为View的生命周期会跟随Activity或Fragment的生命周期,即只要让View感知LifecycleOwner的生命周期变化即可。

但由于RecyclerView的回收复用机制,我们认为每一个ViewHolder应根据回收复用策略,拥有自己的生命周期。

这样就可以像Activity或Fragment一样,利用MVVM模式,来实现UI层与数据层的交互,并通过对LiveData与ViewHolder的改造来保证ViewHolder与数据对应的准确性。

2.目的

使ViewHolder可以像Activity或Fragment那样使用MVVM模式。让ViewHolder拥有生命周期,通过ViewModel与LiveData对数据变化进行监听,在被复用后与原LiveData解绑,解决复用后数据错乱的问题。

3.解决方案

(1)创建抽象类BaseLifecycleViewHolder继承ViewHolder,实现LifecycleOwner接口,使之拥有生命周期。

(2) 创建BaseLifeCycleAdapter继承Adapter,在onBindViewHolder()中注册ViewHolder的生命周期。

(3)创建VHLiveData继承MutableLiveData,保证ViewHolder与数据对应的准确性。

(4)配合使 ViewModel 完成 MVVM 模式。

4.技术实现说明

4.1. BaseLifecycleViewHolder

(1)使BaseLifecycleViewHolder实现LifecycleOwner接口:

private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this@BaseLifeCycleViewHolder)      override fun getLifecycle(): Lifecycle {          return mLifecycleRegistry      } 

(2)为BaseLifecycleViewHolder添加生命周期:

我们认为ViewHolder在创建和被复用的时候应该算作一个新的生命周期的开始,而这两个时机都会走到onBindViewHolder(),所以我们在onBindViewHolder()中注册onCreate和onStart事件。

onStop和onDestroy在itemView的onViewDetachedFromWindow()中注册。但是由于itemView从window中detach后,有可能只是从屏幕中移除但并没有被真正回收,下次滑动移回来将不会走onBindViewHolder(),而是直接走onViewAttachedToWindow(),所以在onViewAttachedToWindow()将会判断ViewHolder的state,如果不处于Start状态,onCreate和onStart会在此时注册。BaseLifeCycleViewHolder的生命周期如下图所示:

代码如下所示:

BaseLifeCycleAdapter:

@Override      public void onBindViewHolder(@NonNull VH holder, int position) {          holder.registerLifecycle(true);          super.onBindViewHolder(holder, position);      } 

BaseLifeCycleViewHolder:

itemView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {          override fun onViewDetachedFromWindow(v: View?) {              mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)              mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)          }  

        override fun onViewAttachedToWindow(v: View?) {              if (mLifecycleRegistry.currentState != Lifecycle.State.STARTED) {                  registerLifecycle(false)              }          }      })

registerLifecycle():

fun registerLifecycle(resetVersion: Boolean) {          mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)          if (resetVersion) {              mViewHolderVersion++ //被复用后ViewHolder的version加1          }          val bindList = bindLiveData(ArrayList, Observer>>())          bindList?.forEach {            it.first?.bindLifecycleOwner(this, it.second!!, resetVersion)        }        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)      }

其中bindLiveData()是个抽象方法,需要子类去实现,为list添加数据

abstract fun bindLiveData(list: ArrayList<Pair<VHLiveData<Any>, Observer>>): ArrayList, Observer>>?  

(3) bindLiveData()的实现:

在registerLifecycle()方法中,我们提供了抽象方法bindLiveData()拿到LiveData与Observer,并调用VHLiveData的bindLifecycleOwner()方法进行绑定。在registerLifecycle()中有个bool型的resetVersion变量,这个变量的作用将在之后进行说明。bindLiveData()的实现如以下示例代码:

@org.jetbrains.annotations.Nullable      @Override      public ArrayListObject>, Observer<Object>>> bindLiveData(@NotNull ArrayListObject>, Observer<Object>>> list) {          list.add(new Pair(mViewModel.getMRoomStatus(), new Observer() {  @Override              public void onChanged(@Nullable Integer status) {                  setRoomText(status);              }          }));  return list;      } 

4.2.原生LiveData源码解析:

在介绍VHLiveData的实现之前,我们先对原生的LiveData源码进行解析,以便更好的理解改造的目的。我们从observe()方法开始:

@MainThread      public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {          if (owner.getLifecycle().getCurrentState() == DESTROYED) {              // ignore              return;          }          LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);          ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);          if (existing != null && !existing.isAttachedTo(owner)) {              throw new IllegalArgumentException("Cannot add the same observer"                      + " with different lifecycles");          }          if (existing != null) {              return;          }          owner.getLifecycle().addObserver(wrapper);      } 

每一次observe()时会将LifecycleOwner和Observer对象封装成一个LifecycleBoundObserver()对象,并放入mObservers这个Map中:

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {          @NonNull final LifecycleOwner mOwner;  

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer observer) {  super(observer);              mOwner = owner;          }  @Override  boolean shouldBeActive() {  return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);          }  @Override  public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {  if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {                  removeObserver(mObserver);  return;              }              activeStateChanged(shouldBeActive());          }  @Override  boolean isAttachedTo(LifecycleOwner owner) {  return mOwner == owner;          }  @Override  void detachObserver() {              mOwner.getLifecycle().removeObserver(this);          }      }

如果LifecycleOwner状态发生了变化,会执行activeStateChanged():

void activeStateChanged(boolean newActive) {              if (newActive == mActive) {                  return;              }              // immediately set active state, so we'd never dispatch anything to inactive              // owner              mActive = newActive;              boolean wasInactive = LiveData.this.mActiveCount == 0;              LiveData.this.mActiveCount += mActive ? 1 : -1;              if (wasInactive && mActive) {                  onActive();              }              if (LiveData.this.mActiveCount == 0 && !mActive) {                  onInactive();              }              if (mActive) {                  dispatchingValue(this);              }          }  

    private void dispatchingValue(@Nullable ObserverWrapper initiator) {          if (mDispatchingValue) {              mDispatchInvalidated = true;              return;          }          mDispatchingValue = true;          do {              mDispatchInvalidated = false;              if (initiator != null) {                  considerNotify(initiator);                  initiator = null;              } else {                  for (Iterator, ObserverWrapper>> iterator =                          mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {                      considerNotify(iterator.next().getValue());  if (mDispatchInvalidated) {  break;                      }                  }              }          } while (mDispatchInvalidated);          mDispatchingValue = false;      }  private void considerNotify(ObserverWrapper observer) {  if (!observer.mActive) {  return;          }  // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.  //  // we still first check observer.active to keep it as the entrance for events. So even if  // the observer moved to an active state, if we've not received that event, we better not  // notify for a more predictable notification order.  if (!observer.shouldBeActive()) {              observer.activeStateChanged(false);  return;          }  if (observer.mLastVersion >= mVersion) {  return;          }          observer.mLastVersion = mVersion;  //noinspection unchecked          observer.mObserver.onChanged((T) mData);  } 

在considerNotify()中,有两个变量需要注意,LiveData持有的mVersion和LifecycleBoundObserver父类ObserverWrapper持有的mLastVersion,这两个变量的默认值都是-1,代码中会判断mLastVersion和mVersion的大小,如果mLastVersion小于mVersion就会走到onChanged()。这个mVersion是在setValue()中赋值的(postValue方法最后也会执行到setValue中):

@MainThread  protected void setValue(T value) {          assertMainThread("setValue");          mVersion++;          mData = value;          dispatchingValue(null);      }

4.3. VHLiveData

如果我们使用原生的LiveData,由于LiveData在ViewHolder复用后还是之前的LiveData对象,所以mVersion的值会根据LiveData之前的setValue的次数增加。mLastVersion的值是在初始化LifecycleBoundObserver()时,在其父类ObserverWrapper中会被赋值为-1,每次setValue后会将mVersion的值赋予mLastVersion。

在上面的代码中我们可知,每一次LiveData和Observer进行绑定时都会新创建一个LifecycleBoundObserver对象,mLastVersion的值为-1。这就导致在ViewHolder复用的时候,mLastVersion是-1,mVersion的值若>-1就会走到onChanged()中。从而导致复用问题。

因此VHLiveData增加了bindLifecycleOwner()方法,用来代替原生的observe()方法,考虑到修改mVersion的值可能会引起多个Observer与LiveData绑定时数据接收的隐患,我们决定在复用时修改mObservers中相应ObserverWrapper持有的mLastVersion变量。

通过反射从mObservers中拿到该Observer对应的LifecycleBoundObserver对象,再将mVersion的值赋予给其父类ObserverWrapper持有的mLastVersion。VHLiveData的代码如下:

public class VHLiveData<T> extends MutableLiveData<T> {  

        public void bindLifecycleOwner(@NonNull LifecycleOwner owner, @NonNull Observer observer, boolean resetVersion) {              super.observe(owner, observer);              if (resetVersion) {                  try {                      Class hySuperClass = LiveData.class;                      Field observers = hySuperClass.getDeclaredField("mObservers");                      observers.setAccessible(true);                      Object objectObservers = observers.get(this);                      Class> classObservers = objectObservers.getClass();                      Method methodGet = classObservers.getDeclaredMethod("get", Object.class);                      methodGet.setAccessible(true);                      Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);                      Object objectWrapper = null;                      if (objectWrapperEntry instanceof Map.Entry) {                          objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();                      }                      if (objectWrapper != null) {                          Class> classObserverWrapper = objectWrapper.getClass().getSuperclass();                          Field lastVersion = classObserverWrapper.getDeclaredField("mLastVersion");                          lastVersion.setAccessible(true);  

                        Field version = hySuperClass.getDeclaredField("mVersion");                          version.setAccessible(true);                          Object objectVersion = version.get(this); //set wrapper's version                          lastVersion.set(objectWrapper, objectVersion);  

                        LogUtil.d("bigcatduan1", "set mLastVersion: " + objectVersion);                      }  

                } catch (Exception e) {                      LogUtil.e("bigcatduan1", "set mLastVersion failed");                      e.printStackTrace();                  }              }          }  

    }

4.4. resetVersion

最后我们再来说说之前遗留的resetVersion这个变量。这个resetVersion是在BaseLifeCycleViewHolder调用bindLiveData()传过来的。

这是因为,如果bindViewHolder()之后,view的生命周期在onViewAttachedToWindow和onViewDetachedFromWindow之间来回切换而并没有被系统回收,这个时候并不会导致复用问题,所以在这种情况下,mLastVersion不用重新赋值。

5.总结

以上方案使各个模块Owner可使用类似Activity或Fragment的方式实现ViewHolder的MVVM模式,并解决ViewHolder复用与异步数据绑定错乱的问题。但是目前我们无法通过ViewModel,实现类似LiveData在Activity和Fragment的数据共享功能,未来会慢慢补充。

参考资料:

[1]https://developer.android.google.cn/reference/androidx/lifecycle/LiveData.html?

[2]https://www.jianshu.com/p/d0ac108b7698

[3]https://www.jianshu.com/p/84f5c9ed0c59

---END---

推荐阅读:
热门Android Studio 插件,这里是Top 20!
Java 编译期与运行期,别傻傻分不清楚!
B站可能真的变了。
谷歌将遏制OEM厂商杀后台问题 安卓11不会加入长截图功能
使用 ConcatAdapter 顺序连接其他 Adapter
大量网友QQ号“被冻结”,苹果安卓都中招!腾讯官方紧急回应……
Kotlin扩展函数 + DSL 封装Adapter,是时候提高你撸RecycleView的效率了!

更文不易,点个“在看”支持一下?

android viewholder里面不执行控件_ViewHolder的MVVM实现相关推荐

  1. android第三方代码,Android--第三方控件--okHttp(示例代码)

    Android中有很多的第三方控件,其中OkHttp是一个很强大的用于网络加载的第三方控件,当然了,它的内部也是使用原生的代码封装好的.今天我们就来看一下OkHttp的简单用法: 说到网络请求,肯定就 ...

  2. Android微信九宫格图片展示控件

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/214 Android微信九宫格图片展示控件 半年前,公司产 ...

  3. android+高仿视频录制,Android高仿微信拍照控件,实战推荐!

    原标题:Android高仿微信拍照控件,实战推荐! 作者:陈嘉桐 转自:ttps://github.com/CJT2325 控件介绍 不知道是不是在微信更新到6.0版本之后,微信将它的拍照和录制视频的 ...

  4. Android开发之日期时间控件选择

    Android开发之日期时间控件选择 文章目录 前言 一.创建弹出Layout 1.1 新建Layout,修改样式为LinearLayout 1.2 Layout中添加日期和时间控件 二.新建Date ...

  5. Study on Android【四】--显示控件使用

    Android的界面显示同样也是基于控件的.通常是用View(包括ViewGroup)控件配上XML的样式来做的.具体细节不想说了,可以参考 Samples里的ApiDemos/View,和View的 ...

  6. android 电量控件,Android实现显示电量的控件代码

    下面介绍了Android实现显示电量的控件代码,具体代码如下: 1.目录结构,本人是使用安卓死丢丢. 2.运行界面,输入框中输入数值,点击刷新,会再电池中显示出相应的电量 3.绘制自定义电池控件,首先 ...

  7. Android:Socket客户端开发,Android 的Socket客户端优化,Android非UI线程修改控件程序崩溃的问题

    一.Android:Socket客户端开发 创建一个工程 我们要做的是按下按键之后,去往服务器 (服务器) 或者我们自己写的服务器 ,给他发送一些预定好的东西 然后打开操作界面 然后修改一下 你要发送 ...

  8. Android开发三:常用控件1--TextView、EditText、Button

    上一节写到android的工程目录结构,这一节继续,开始学习控件,中间的跨度挺大,关于Activity和intent的知识我就略过了,原因很简单,网上的关于那个的资料特别多,而且理论的东西我这都是了解 ...

  9. 让一个图片填满一个控件_如何在Android中实现一个全景图控件(二)

    一.背景 在 如何在Android中实现一个全景图控件(一)中,介绍了项目的一些基本情况(有 demo 演示),如果项目对你有帮助,希望文章赏个赞,项目 star 一下. 项目地址:https://g ...

最新文章

  1. 网络营销外包立足用户角度完成企业网站网络营销外包优化
  2. Objective-C 编码规范
  3. CVE-2017-7921复现(解密文件)
  4. 新网域名查询和注册API接口类 源码
  5. linux traceroute命令详解_详解Linux系统路由跟踪指令traceroute语法、工作原理和实例说明...
  6. FairScheduler的任务调度机制——assignTasks
  7. mysql server启动_mysql的启动方式
  8. python-斐波那契数列的计算
  9. Zsh和oh my zsh的安装和使用
  10. 《流畅的python》概述
  11. Mac OS怎么删除虚拟机声卡
  12. eclipse jade插件安装
  13. matlab ansys对应版本,ANSYS所有版本下载链接
  14. 基于c#的海量图片查重去重
  15. python 在线编译器
  16. 【JVM】运行时数据区概述(程序计数器、虚拟机栈、本地方法栈)
  17. Apache ab性能测试结果分析
  18. 机房迁移测试时需要注意事项
  19. 龙佰集团拟35亿投建20万吨锂电负极项目 钛白粉龙头转型可期
  20. 基于STM32F103C8T6的MPU6050调试与数字运动处理器DMP

热门文章

  1. 【微信开发】-- 企业转账到用户
  2. C++ 虚函数与多态
  3. windows .bat批处理实现进程监控确保程序运行
  4. C语言__LINE__实现原理
  5. 机器学习总结之第一章绪论
  6. 作业四:灯泡异常问题
  7. CISC, RISC 探究
  8. Java中的锁(转)
  9. mysql调优 参数说明
  10. 推荐算法之用户推荐(UserCF)和物品推荐(ItemCF)对比