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 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");
    }
    //用来获取对应 ViewModel 实例的,保证了同一个 Key 取出是同一个 ViewModel
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
//ViewModelProvider.java
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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<String, ViewModel> 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<String> keys() {
        return new HashSet<>(mMap.keySet());
    }
  • 3)、NewInstanceFactory
    通过反射,直接创建了ViewModel对象
//ViewModelProvider.java 中的 AndroidViewModelFactory.java
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);
    }
}
  • 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<String, ViewModel> 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<String> 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<String>? = null

    fun getUsers(): LiveData<String>? {
        if (users == null) {
            users = MutableLiveData<String>()
            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)
        })
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266