ViewHolder 的 MVVM实现

栏目: IT技术 · 发布时间: 6年前

本文字数: 1569

预计阅读时间: 5分钟

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的生命周期如下图所示:

ViewHolder 的 MVVM实现

代码如下所示:

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<Pair<VHLiveData<Any>, Observer<Any>>>())  
        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<Any>>>): ArrayList<Pair<VHLiveData<Any>, Observer<Any>>>?  

(3) bindLiveData()的实现:

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

@org.jetbrains.annotations.Nullable  
    @Override  
    public ArrayList<Pair<VHLiveData<Object>, Observer<Object>>> bindLiveData(@NotNull ArrayList<Pair<VHLiveData<Object>, Observer<Object>>> list) {  
        list.add(new Pair(mViewModel.getMRoomStatus(), new Observer<Integer>() {  
            @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<T> 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<T> 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<Map.Entry<Observer<T>, 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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms Unlocked

Algorithms Unlocked

Thomas H. Cormen / The MIT Press / 2013-3-1 / USD 25.00

Have you ever wondered how your GPS can find the fastest way to your destination, selecting one route from seemingly countless possibilities in mere seconds? How your credit card account number is pro......一起来看看 《Algorithms Unlocked》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试