一、运用架构

1.1、规划意图

运用或许规划某个运用架构的意图是什么?

简略的概括来说,是为了满足开闭准则,在不修正原有代码的状况下给程序扩展功能,而不是直接修正原有代码。

终究的意图是为了 提高开发测试效率,降低程序保护本钱(降本增效)

1.2、架构挑选

依据上面的认知,咱们挑选运用一些其他的规划来完成开闭准则的意图。包括但不限于:单一责任,笼统接口,承继多态、解耦等等办法。

Android官方引荐的MVVM运用架构(现在现已不是官方最引荐架构了),主要是经过拆分View层(Activity/Fragmet)责任,简化View层的逻辑,分离View层和Model层之间的耦合的办法来完成开闭准则的意图

MVVM架构模型如下(每个组件仅依靠下一级组件):

Android架构组件使用和原理分析:ViewModel+LiveData

这儿需求强调的是:

  • 虽然该架构本身是完成程序开闭准则的比较好实践之一,但是并不是一切的运用界面代码规划有必要运用该架构。

  • 运用架构的挑选应该依据实际状况挑选,没有最好的架构,只是只要当时场景的最优挑选


下面主要剖析下该架构下最重要的两个架构组件ViewModel和LiveData的运用和原理

二、组件运用

ViewModel教程

LiveData教程

简略运用示例(具体教程可查看如上官方教程):

ViewModel和LiveData一般组合运用(也能够独自运用,依据实际场景挑选):

界面由MainActivity和对应的布局activity_main.xml组成。

首页需求恳求页面数据,咱们运用HomeViewModel恳求和存储这些数据,界说了以下文件:

MainActivity

activity_main.xml

HomeViewModel

以下代码段显现了这些文件的起始内容(为简略起见,省掉了布局文件)

MainActivity:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Use the 'by viewModels()' Kotlin property delegate from the activity-ktx artifact
        // or use ' ViewModelProvider(this).get(HomeViewModel::class.java)'
        val viewModel: HomeViewModel by viewModels()
        viewModel.pageData.observe(this, { data ->
            Log.d("mumu","data: $data")
        })
    }

HomeViewModel:

class HomeViewModel : ViewModel() {
    val pageData: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
    init {
        // 恳求回来字符串并保存
        requestNetData(object : IHttpCallBack {
            override fun onError(error: IHttpCallBack.HttpError?) {
                pageData.value = null
            }
            override fun onEnd(data: String) {
                pageData.value = data
            }
        })

阐明:

  • 在ViewModel层调用网络数据层的办法恳求数据并保存到ViewModel的LiveData存储器中。
  • 在View层(Activity)中注册调查者目标,调查LiveData的数据更新。无需在onStop中止调查,由于LiveData结构具有生命周期感知,检测调查者onStop了不会告诉调查者数据更新。

三、组件原理

3.1、LiveData

LiveData 是一种可调查的数据存储器。运用中的其他组件能够运用此存储器监控目标的更改,而无需在它们之间创立明确且严格的依靠路径。LiveData 组件还能感知运用组件(如 Activity、Fragment 和 Service)的生命周期状况,能够主动整理调查者以避免目标走漏和过多的内存消耗。

3.1.1、注册调查者

@MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        //运用组件现已毁掉,疏忽 
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        //将调查者目标封装成一个LifecycleBoundObserver目标(详见3.1.2)
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        //不为空阐明现已在SafeIterableMap调集中了,同一个运用组件(Activity/Fragment)不能重复增加相同的调查者
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        //LifecycleBoundObserver完成LifecycleEventObserver接口,绑定Lifecycle的生命周期
        owner.getLifecycle().addObserver(wrapper);
    }

阐明: 该步骤主要讲调查者目标observer封装成一个LifecycleBoundObserver目标,然后保存到调集中,并且经过LifeCycle绑定运用组件的生命周期

3.1.2、LifecycleBoundObserver调查者包装类

//内部类
    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }
        //判别调查者是否激活,运用组件生命周期处在onStart和onResume状况该办法才会回来true
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
        //运用组件生命周期发生改动时回调该办法(Lifecycle组件的功能)
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
            //判别运用组件现已destroy了则主动解注册
            if (currentState == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            Lifecycle.State prevState = null;
            while (prevState != currentState) {
                prevState = currentState;
                //状况办法改动,调用该办法改动LiveData状况和分发数据(详见3.1.3)
                activeStateChanged(shouldBeActive());
                currentState = mOwner.getLifecycle().getCurrentState();
            }
        }
        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }
        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }
//LiveData的解注册办法(LiveData的办法)
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
    assertMainThread("removeObserver");
    //从调集中移除调查者包装目标
    ObserverWrapper removed = mObservers.remove(observer);
    //为空阐明现已移除了
    if (removed == null) {
        return;
    }
    //Lifecycle解注册
    removed.detachObserver();
    //调用activeStateChanged办法同步状况改动
    removed.activeStateChanged(false)

3.1.3、activeStateChanged状况改动同步办法

void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
  //该办法是经过加一减一来判别当时LiveData目标内是否有处在调集状况的调查者
  //假如从无到有则调用onActive办法,从有到无则调用onInactive办法
  //两个办法均为LiveData的空完成办法,咱们能够做一些初始化或许数据重置的操作
            changeActiveCounter(mActive ? 1 : -1);
            if (mActive) {
 //真实开发分发数据的办法(只要调查者处于激活状况才会分发数据,详见3.1.4)
                dispatchingValue(this);
            }
        }

3.1.4、dispatchingValue分发数据办法

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            //mDispatchInvalidated符号数据是否失效,假如失效从头分发最新数据
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
            //状况改动同步数据initiator不为空(首次注册状况下假如需求同步也是走到这儿)
                considerNotify(initiator);
                initiator = null;
            } else {
                //更新数据状况同步一切调查者数据改动
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
               //告诉调查者数据改动分发(详见3.1.5)
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

3.1.5、considerNotify告诉调查者数据改动办法

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;
        }
      //判别数据是否现已告诉了该调查者目标,避免重复更新(LiveData数据每更新一次,mVersion就会加一)
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //没有告诉过则调用调查者的onChanged办法告诉数据更新
        observer.mObserver.onChanged((T) mData);
    }

阐明:

走到这儿,LiveData的一次完整数据调查和数据更新流程就完毕了。

经过上述流程可知,LiveData会经过Lifecycle主动观测运用组件生命周期改动,组件毁掉了主动解注册;组件激活了将调查者变为激活状况并及时同步数据给调查者。调查者只需求在数据更新办法中做相应的中心逻辑处理,而无需关心数据丢失或许数据重复回调,也无需担心内存走漏,LiveData会主动解注册调查者。

终究还有就是LiveData的数据更新办法(setValue和postValue办法),详见3.1.6阐明

3.1.6、更新数据办法

setValue或许postValue(区别是postValue支持异步调用,终究仍是会切换到主线程调用setValue办法)

@MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
//数据改动,version加一
        mVersion++;
        mData = value;
//调用3.1.4的分发数据办法,不过这儿的调查者包装类目标为null因而会循环调集中一切的调查者目标告诉数据更新
        dispatchingValue(null);
    }

3.2、ViewModel

ViewModel目标为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理事务逻辑,以与模型进行通讯。例如,ViewModel能够调用其他组件来加载数据,还能够转发用户恳求来修正数据。ViewModel无法感知界面组件,因而不受装备更改(如在旋转设备时从头创立 Activity)的影响

ViewModel是怎么确保同一个Activity目标仅有一个ViewMode目标?

ViewModel目标创立是经过ViewModelProvider目标的get办法来进行创立的,经过ViewModelProvider目标来确保ViewModel的唯一

3.2.1、ViewModelProvider结构办法

ViewModelProvider共有三个结构办法,如下:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
 //ViewModelStoreOwner是个接口只要一个办法getViewModelStore,由Activity/Fragment完成(详见3.2.2)
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
 //终究会调用的该结构办法,ViewModelStore封装了HashMap调集保存ViewModel目标
 //ViewModelStore详见3.2.3
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store; 

3.2.2、完成ViewModelStoreOwner接口

下列代码已Activity为例,Fragment相似

@NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
 //mViewModelStore是ComponentActivity的成员变量,内部经过HashMap存储数据ViewModel
        if (mViewModelStore == null) {
            //假如为空,先尝试从mLastNonConfigurationInstances中取,
            //mLastNonConfigurationInstances是activity的成员变量,这个值在Acivity attach 的时候赋值
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
//假如取值为空则从头创立一个
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

3.2.3、ViewModelStore类

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }
    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();

阐明: 其实就是个HashMap,用来保存ViewModel目标

3.2.4、ViewModelProvider.get(ViewModel.class)办法获取ViewModel实例

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
        //优先从ViewModelStore中获取,不为空则直接回来
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
       //为空则经过mFactory创立,
       //这儿的mFactory假如ViewModeStoreOwner没有完成HasDefaultViewModelProviderFactory
       //则默许运用NewInstanceFactory经过反射构建ViewModel目标。
       //Componentactivity默许是完成了HasDefaultViewModelProviderFactory接口的,回来  SavedStateViewModelFactory,
       //假如是ViewModel是无参结构则终究仍是会经过NewInstanceFactory构建目标
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

3.2.5、NewInstanceFactory工厂类

NewInstanceFactory经过反射构建ViewModel目标,办法比较简略,其他工厂相似

    public static class NewInstanceFactory implements Factory {
        private static NewInstanceFactory sInstance;
        @NonNull
        static NewInstanceFactory getInstance() {
            if (sInstance == null) {
                sInstance = new NewInstanceFactory();
            }
            return sInstance;
        }
        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }

阐明: 上述5个步骤即确保了ViewModel目标在Activity/Fragment的唯一性

3.2.6、装备改动导致Activity重建状况保存

怎么确保装备状况改动导致Activity重建的状况下,ViewModel不变?

主要是靠NonConfigurationInstances目标,代码阐明如下

    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = this.onRetainCustomNonConfigurationInstance();
        ViewModelStore viewModelStore = this.mViewModelStore;
        ComponentActivity.NonConfigurationInstances nci;
        if (viewModelStore == null) {
            nci = (ComponentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
            if (nci != null) {
                viewModelStore = nci.viewModelStore;
            }
        }
        if (viewModelStore == null && custom == null) {
            return null;
        } else {
            nci = new ComponentActivity.NonConfigurationInstances();
            nci.custom = custom;
            //保存viewModelStore
            nci.viewModelStore = viewModelStore;
            return nci;
        }

四、实践总结

4.1、内存走漏

1、 ViewModel不能持有Activity/Fragment/View相关目标的引证,由于ViewModel生命周期和组件的生命周期不一致,这种状况下或许(少量状况,由于一般咱们不允许装备更新导致Activity重建,但是即使仅从规划模式的视点考虑咱们也应该这么做)会导致内存走漏。

2、 LiveData.observer办法注册调查者的时候,留意假如是Fragment和View的状况。Fragment建议传递Fragment本身作为LifecycleOwner(和Activity的LifecycleOwner不是同一个)由于Fragment或许会先于 activity毁掉。

3、组件真实毁掉时,会调用ViewModel的onCleared办法,留意毁掉必要的数据,比如说数据层的监听回调等等(或许在数据层运用弱运用,不是有必要,需求留意数据层生命周期很长的状况,例如数据层是个单例,一直持有ViewModel的引证会导致ViewModel无法回收)

4.2、数据保存

1、 Activity/Fragment 或许Fragment/Frament之间传递数据,留意传递的ViewModelStore需求时同一个,由于Fragment也会完成ViewModelStoreOwner接口,经过Fragment的getViewModelStore办法和Activity的getViewModelStrore办法获取的ViewModelStore并不是同一个,所以终究获取到的ViewModel目标并不是同一个。Fragment共享数据需求相同的Activity作为ViewModelStoreOwner,建议运用requireActivity办法获取Acitivity。

2、 ViewModel只能在装备更新导致的Activity重建的状况下保存,例如屏幕旋转。并不能在异常毁掉Activity的状况保存数据,例如操作系统由于内存紧张杀掉进程的状况,这种状况数据并不能经过ViewModel康复数据,能够考虑运用onSaveInstanceState办法或许其他办法保存数据。