ViewModel使用到源码解读

介绍

ViewModel 是一个类,负责为 Activity 或 Fragment 准备和管理数据。它还处理 Activity Fragment 与应用程序其余部分的通信(例如调用业务逻辑类)。 ViewModel 始终与范围(片段或活动)关联创建,并且只要范围处于活动状态就会保留。例如。如果它是一个活动,直到它完成。换句话说,这意味着如果 ViewModel 的所有者因配置更改(例如旋转)而被销毁,则不会销毁 ViewModel。新的所有者实例只是重新连接到现有模型。 ViewModel 的目的是获取和保存 Activity 或 Fragment 所需的信息。 Activity 或 Fragment 应该能够观察到 ViewModel 的变化。 ViewModel 通常通过 LiveData 或 Android 数据绑定公开这些信息。您还可以使用您喜欢的框架中的任何可观察性构造。 ViewModel 的唯一职责是管理 UI 的数据。它永远不应访问您的视图层次结构或保留对 Activity 或 Fragment 的引用。

使用

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView = findViewById<TextView>(R.id.text1)

        val viewModelProvider = ViewModelProvider(this)
        val mainViewModel = viewModelProvider.get(MainViewModel::class.java)
        mainViewModel.stringMutableLiveData.observe(this) {
            Log.e(TAG, "onCreate: 收到数据...")
            textView.text = Gson().toJson(it)
        }

        findViewById<View>(R.id.btn).setOnClickListener {
            Log.e(TAG, "onCreate: 发出网络请求...")
            mainViewModel.getWeChatPublicAccount()
        }
    }
}
public class MainViewModel extends ViewModel {

    private MutableLiveData<WeChatPublicAccountBean> stringMutableLiveData = new MutableLiveData<>();

    public MutableLiveData<WeChatPublicAccountBean> getStringMutableLiveData() {
        return stringMutableLiveData;
    }

    public void getWeChatPublicAccount() {
        NetworkManager.INSTANCE.create(WanAndroidApi.class)
                .getWeChatPublicAccount()
                .compose(RxUtil.io2main())
                .subscribe(new SimpleObserver<WeChatPublicAccountBean>() {
                    @Override
                    public void oNext(WeChatPublicAccountBean weChatPublicAccountBean, Disposable disposable) {
                        stringMutableLiveData.setValue(weChatPublicAccountBean);
                    }
                });
    }
}

运行效果

这是进入界面点击了一下按钮打印的日志

 

 接下来横竖屏切换一下

 

 从上面日志上可以看到只有最开始点击按钮发了网络请求,横竖屏切换后直接在LiveData#observer中就收到了数据(LiveData下篇文章讲解),接下里我们从源码上看一下ViewModel的实现

构造

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

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

构造上发现创建ViewModel对象需要ViewModelStore,Factory在我们的demo中使用的是单参数构造我们继续看到owner#getViewModelStore的调用

ComponentActivity#getViewModelStore

    public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            //注释1
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                mViewModelStore = nc.viewModelStore;
            }
            //注释2
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

我们构造时传入的是this,也就是当前MainActivity对象,MainActivity的间接父类ComponentActivity就实现了ViewModelStoreOwner接口所有owner#getViewModelStore调用到的就是我们上面贴出的这段源码

上面源码中可以看到单个的Activity对象只会有一个ViewModelStore对象,我们先看到注释1,这里尝试获取一个NonConfigurationInstances对象但正常情况下这个对象是为null的,只有在我们进行了横竖屏切换时这个对象才是存在的我们继续看下对这个对象是如何进行保存的

    / ** Called by the system, as part of destroying an
      activity due to a configuration change, when it is known that a new
      instance will immediately be created for the new configuration.  You
      can return any object you like here, including the activity instance
      itself, which can later be retrieved by calling
      {@link #getLastNonConfigurationInstance()} in the new activity
      instance. **/



       public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        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;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

 结合上面的注释得知了onRetainNonConfigurationInstance在横竖屏切换时会由系统调用我们可以在这里保存一些数据,并且我们可以调用getLastNonConfigurationInstance获得之前保存的对象,ViewModel就是通过这个函数保存了我们之前的ViewModelStore对象,就是说不管横竖屏切换多少次我们每次获取的ViewModel对象都是同一个,这就是ViewModel横竖屏切换不需要重新请求数据的秘密了

接下来是ViewModelProvider构造的第二个参数ViewModelProvider#Factory

因为ComponentActivity中同样实现了HasDefaultViewModelProviderFactory接口所有我们继续看到ComponentActivity#HasDefaultViewModelProviderFactory源码

ComponentActivity#HasDefaultViewModelProviderFactory

    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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 (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

这里没啥好说的就是返回了一个SavedStateViewModelFactory对象

我在再往下看viewModelProvider是如何获取对应ViewModel的

viewModelProvider#get

    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);
    }



    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        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.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

这里的代码就相对简单了就是如果在原先的ViewModelStore中存在对应class的ViewModel就直接返回,没有则用Factory进行创建并保存到ViewModelStore中下次就可以直接获取不用再次新建了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值