ViewModel

Android知识总结

一、什么是ViewModel

ViewModel类指在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。在对应的作用域内,保正只生产出对应的唯一实例,保证UI组件间的通信。

由于 ViewModel 生命周期可能长与Activity 生命周期,所以为了避免内存泄漏,Google 禁止在 ViewModel 中持有 Context 、Activity 或 View 的引用。如果一定需要使用 Context, 可以继承 AndroidViewModel 类,内部维护了一个 ApplicationContext

Android中的ViewModel是一个可以用来存储UI相关的数据的类。ViewModel 一般要配合 LiveDataDataBinding一起使用。

  • 特点
    1)、通过定义我们可以得出

  • ViewModel不会随着Activity的屏幕旋转而销毁;

  • 在对应的作用域内,保正只生产出对应的唯一实例,保证UI组件间的通信
    2)、重点说一下ViewModel和onSaveInstanceState的关系

  • 对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

  • ViewModel存储大量数据,不用序列化与反序列化

  • onSaveInstanceState存储少量数据

  • 相辅相成,不是替代

  • 进程关闭是onSaveInstanceState的数据会保留,而ViewModel销毁

二、ViewModel中各个类

  • ViewModelStoreOwner:是一个接口,用来获取一个ViewModelStore对象
  • ViewModelStore存储多个ViewModel,一个ViewModelStore的拥有者( Activity )在配置改变, 重建的时候,依然会有这个实例
  • ViewModel:一个对 Activity、Fragment 的数据管理类,通常配合 LiveData 使用
  • ViewModelProvider创建一个 ViewModel 的实例,并且在给定的ViewModelStoreOwner中存储 ViewModel

三、源码分析

  • 1)、ViewModelProvider
    ViewModelProvider构造方法
private final Factory mFactory;
private final ViewModelStore mViewModelStore;

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}
  • 通过 ViewModelStoreOwner获取ViewModelStore对象并给 mViewModelStore赋值
  • 给mFactory赋值,这里赋值的是NewInstanceFactory这个对象

ViewModelProvider的 get 方法

private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";

public  T get(@NonNull Class modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    //用来获取对应 ViewModel 实例的,保证了同一个 Key 取出是同一个 ViewModel
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
//ViewModelProvider.java
public  T get(@NonNull String key, @NonNull Class modelClass) {
    //从ViewModelStore中,根据 key,取一个 ViewModel
    ViewModel viewModel = mViewModelStore.get(key);
    //判断取出来的 ViewModel 实例和传进来的是否是一个,是同一个,直接返回此缓存中实例
    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.
        }
    }
    //通过Factory创建一个ViewModel
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    //把新创建的ViewModel用ViewModelStore存储起来,以备下次使用,最后返回新创建的ViewModelStore
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}
  • 2)、ViewModelStore的源码
public class ViewModelStore {
     //声明一个 Map 来存储ViewModel
    private final HashMap mMap = new HashMap<>();
     //存储ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    //取出 ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set keys() {
        return new HashSet<>(mMap.keySet());
    }
  • 3)、NewInstanceFactory
    通过反射,直接创建了ViewModel对象
//ViewModelProvider.java 中的 AndroidViewModelFactory.java
public  T create(@NonNull Class 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);
    }
}
  • 4)、ViewModelStoreOwner
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

一个接口,里面一个方法返回了ViewModelStore对象,它的实现类在 AndroidX 中ComponentActivity和 Fragment。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements ViewModelStoreOwner,XXX{
   
    private ViewModelStore mViewModelStore;

    @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.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            //创建了一个ViewModelStore并返回了
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    } 
}

四、总结

  • 1)、分析为啥ViewModel不会随着Activity的屏幕旋转而销毁

首先知道的是 ViewModel 不被销毁,是在一个 ViewModelStore 的 Map 中存着呢,所以要保证ViewModelStore不被销毁。

主要是通过onRetainNonConfigurationInstance方法把ViewModelStore缓存;在NonConfigurationInstances中,在getViewModelStore取出ViewModelStore。用NonConfigurationInstancesViewModelStore存储和读取。

  • onRetainNonConfigurationInstance方法存储
static final class NonConfigurationInstances {
    Object custom;
    //存储 ViewModel
    ViewModelStore viewModelStore;
}

@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }
    //1把ViewModel存储在 NonConfigurationInstances 对象中
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}
  • getViewModelStore方法获取
@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.");
    }
    if (mViewModelStore == null) {
        //1获取了NonConfigurationInstances一个对象,不为空从其身上拿一个ViewModelStore,这个就是之前保存的ViewModelStore
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

当 Activity 重建时还会走到getViewModelStore方法,这时候就是在NonConfigurationInstances拿一个缓存的ViewModelStore。

  • 2)、onCleared方法在什么调用

ComponentActivity的构造方法

public ComponentActivity() {
    Lifecycle lifecycle = getLifecycle();
    
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            //生命周期为销毁状态
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {
                    //清楚 ViewModel
                    getViewModelStore().clear();
                }
            }
        }
    });
}

在ComponentActivity的构造方法中,我们看到,在 Activity 的生命周期为 onDestory的时候,并且当前不是,配置更改(比如横竖屏幕切换)就会调用ViewModelStore 的 clear 方法,进一步回调用 ViewModel 的onCleared方法。

  • 3)、为什么在对应的作用域内,保正只生产出对应的唯一实例

ViewModelStore的 get方法,是根据key 来取值的,如果 key相同,那取出来的ViewModel就是一个。

ViewModelStore的源码

public class ViewModelStore {
     //声明一个 Map 来存储ViewModel
    private final HashMap mMap = new HashMap<>();
     //存储ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    //取出 ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set keys() {
        return new HashSet<>(mMap.keySet());
    }

    //表明要清空存储的数据,还会调用到ViewModel的 clear 方法,也就是最终会调用带 ViewModel 的onCleared()方法
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

注意在从 Map中去 ViewModel 的时候是根据 Key,也就是拼接的那个字符串DEFAULT_KEY + ":" + canonicalName。这也就解释为什么在对应的作用域内,保正只生产出对应的唯一实例。

    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";

五、ViewModel简单使用

5.1 继承ViewMode,实现自定义ViewModel。

class MyViewModel : ViewModel() {
    private var users: MutableLiveData? = null

    fun getUsers(): LiveData? {
        if (users == null) {
            users = MutableLiveData()
            setName()
        }
        return users
    }

    fun setName() {
        users?.value = "onexzgj"
    }
}

5.2 使用ViewModel

class ViewModelActivity : AppCompatActivity() {
  lateinit var viewmodel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        setTitle("ViewModel的使用")

        viewModel =  ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
        viewModel.getUsers()?.observe(this, Observer {
            Log.d("ViewModel", it)
        })
    }
}

你可能感兴趣的:(ViewModel)