JetPack系列(三)ViewModel

如遇图片无法查看,点击此链接

  • ViewModel是什么 具有宿主生命周期感知能力的数据存储组件 ViewModel保存的数据在页面因配置变更导致页面销毁重建后依然存在
  • ViewModel怎么使用
  • ViewModel为什么能在配置变更后保存数据
  • ViewModel结合SaveState后做到页面非正常关闭保存数据
  • ViewModel在Activity之间共享数据
  • ViewModel在Fragment之间共享数据

ViewModel

ViewModel具有宿主生命周期感知能力的数据存储组件 ViewModel保存的数据在页面因配置变更导致页面销毁重建后依然存在

ViewModel怎么使用

class ViewModelActivity : AppCompatActivity() {
    val viewModel : TestViewModel by lazy {
        ViewModelProvider(this)[TestViewModel::class.java]
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_model_activity)
        viewModel.testLiveData.observe(this){
            Log.d("ViewModel数据", it)
        }
        viewModel.initData()
    }
}
class TestViewModel : ViewModel(){
    val testLiveData  = MutableLiveData<String>()
    fun initData() : LiveData<String>{
        //检查LiveData中是否存在数据,存在就复用,不存在重新生成
        if(testLiveData.value == null){
            //使用随机数,ViewModel保存数据,页面因配置销毁后,展示的数据应该相同
            //TODO 业务请求,数据库请求
            testLiveData.value = Random().nextFloat().toString()
        }
        return testLiveData
    }
}

ViewModel原理

我们从ViewModelProvider中获取ViewModel实例主要使用到以下的几个方法初始化函数最终都会调用到最后一个构造函数。我们使用的函数都接受一个ViewModelStoreOwner的参数,这是因为我们使用的Activity继承类ViewModelStoreOwner这个接口

当我们使用get获取ViewModel的时候,首先会将传入进来的Class取出全类名并且拼接上前缀,再进入mViewModelStore中寻找ViewModel对应的实例,如果有取出,没有则新建。

这里可以看到ViewModel实例是保存在mViewModelStore中,而mViewModelStore的提供者是ViewModelStoreOwner,所以ViewModel最终的保存操作是在ViewModelStoreOwner中,也就是在Activity中。

public class ViewModelProvider{
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner){}
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull androidx.lifecycle.ViewModelProvider.Factory factory) { }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull androidx.lifecycle.ViewModelProvider.Factory factory) { }
    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);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof androidx.lifecycle.ViewModelProvider.OnRequeryFactory) {
                ((androidx.lifecycle.ViewModelProvider.OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof androidx.lifecycle.ViewModelProvider.KeyedFactory) {
            viewModel = ((androidx.lifecycle.ViewModelProvider.KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
}

Actvity中是在哪里保存mViewModelStore的呢,由于方法太多,我只提供方法名称。
AppCompatActivity继承自ComponentActivity,ComponentActivity实现了ViewModelStoreOwner
onRetainNonConfigurationInstance方法中提供了保存的实现,该方法由Activity中retainNonConfigurationInstances方法调用。Activity中的方法又在ActivityThread中被handleDestroyActivity方法调用

Fragment为何能共享Activity中的ViewModel数据

/**
 * D/Fragment-ViewModel数据: 0.19282305
D/ViewModel数据: 0.19282305
旋转后
D/Fragment-ViewModel数据: 0.19282305
D/ViewModel数据: 0.19282305
 */
class ViewModelActivity : AppCompatActivity() {
    val viewModel: TestViewModel by lazy {
        ViewModelProvider(this)[TestViewModel::class.java]
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_model_activity)
        viewModel.testLiveData.observe(this) {
            Log.d("ViewModel数据", it)
        }
        viewModel.initData()
        val fragment = supportFragmentManager.findFragmentById(R.id.testFragment)
        if(fragment == null){
            supportFragmentManager.beginTransaction()
                .add(R.id.testFragment, TestFragment.getInstance()).commitNowAllowingStateLoss()
        }
    }
}

class TestViewModel : ViewModel() {
    val testLiveData = MutableLiveData<String>()
    fun initData(): LiveData<String> {
        //检查LiveData中是否存在数据,存在就复用,不存在重新生成
        if (testLiveData.value == null) {
            //使用随机数,ViewModel保存数据,页面因配置销毁后,展示的数据应该相同
            //TODO 业务请求,数据库请求
            testLiveData.value = Random().nextFloat().toString()
        }
        return testLiveData
    }
}

class TestFragment : Fragment() {
    companion object {
        fun getInstance(): Fragment {
            return TestFragment()
        }
    }

    val fragmentViewModel: TestViewModel by lazy {
        ViewModelProvider(requireActivity())[TestViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View? {
        fragmentViewModel.testLiveData.observe(viewLifecycleOwner, {
            Log.d("Fragment-ViewModel数据", it)
        })
        return inflater.inflate(R.layout.view_model_activity, container, false)
    }
}

从上面的代码中可以看到,如果我们的Activity中持有Fragment,且Fragment中获取ViewModel的方式为

ViewModelProvider(requireActivity())[TestViewModel::class.java]

则Fragment中的ViewModel实例和Activity中是同一个实例。

上面介绍过ViewModel的保存者是mViewModelStore,而mViewModelStore的提供者是ViewModelStoreOwner,而在Fragment中我们使用的是Fragment中保存的Activity,所以ViewModelStoreOwner是同一个对象,所以ViewModel是同一个对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值