关于android:android-JetpackViewModel使用方法和详细原理解析

33次阅读

共计 7322 个字符,预计需要花费 19 分钟才能阅读完成。

关注我的公众号 “安安安安卓” 收费学常识

大部分人更关怀用法,所以我先讲用法,再讲对 viewmodel 的了解,最初讲源码

1、ViewModel 初始化形式

来到 androidx,ViewModel 的创立形式与老版本有了很大的不同,所以这里还是要将 Viewmodel 的初始化讲一下

1.1、安卓工厂初始化

每次都会从新创立 model,并且不受 ViewModelStore 管控, 所以无非凡需要禁止应用该种形式

应用 AndroidViewModelFactory 工厂创立

  1. viewmodel 类定义
class AndroidFactoryViewModel(app:Application): AndroidViewModel(app) {fun print(){logEE("应用安卓默认工厂创立 viewmodel")
    }
}
  1. 创立 viewmodel 实例代码
val model = ViewModelProvider.AndroidViewModelFactory.getInstance(application)
                .create(AndroidFactoryViewModel::class.java)
            model.print()

1.2、简略工厂初始化 ViewModel

每次都会从新创立 model,并且不受 ViewModelStore 管控, 所以无非凡需要禁止应用该种形式

NewInstanceFactory

  1. viewmodel 类定义代码
class SimpleFactoryViewModel: ViewModel() {fun print(){logEE("应用简略工厂创立 viewmodel")
    }
}
  1. 创立 viewmodel 代码
 val model =ViewModelProvider.NewInstanceFactory().create(SimpleFactoryViewModel::class.java)
 model.print()

1.3、自定义安卓工厂初始化

屡次创立能够复用 model,不会从新创立

默认的工厂只能创立带 Application 的 ViewModel 实例的,咱们通过自定义工厂实现自定义结构参数的目标

  1. 定义安卓工厂
class CustomAndroidViewModelFactory(val app: Application, private val data: String) :
    ViewModelProvider.AndroidViewModelFactory(app) {override fun <T : ViewModel?> create(modelClass: Class<T>): T {if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
            return try {modelClass.getConstructor(Application::class.java, String::class.java)
                    .newInstance(app, data)
            } ...
        }
        return super.create(modelClass)
    }
}

省略了 catch 中的代码,您能够到源码中查看

在咱们的自定义工厂的结构中传入一个自定义的 data 参数,代表咱们的自定义结构参数

  1. viewmodel 类定义

    必须是继承 AndroidViewModel 才能够

class CustomAndroidViewModel(private val app: Application, private val data: String) :
    AndroidViewModel(app) {fun print() {logEE(data)
    }
}

咱们的 CustomAndroidViewModel 类中也有一个 data:String 参数,这个参数就是对应上一步中自定义工厂中的 data 参数

  1. 实例化 viewmodel
val model = ViewModelProvider(
                viewModelStore,
                CustomAndroidViewModelFactory(application, "自定义安卓工厂创立 viewmodel")
            ).get(CustomAndroidViewModel::class.java)
            model.print()

1.4、自定义简略工厂初始化

屡次获取能够复用 model,不会从新创立

自定义简略工厂也是为了实现 viewmodel 结构传参的目标,废话不多说,间接上代码吧

  1. 定义安卓工厂
class CustomSimpleViewModelFactory(app:Application,private val data:String) : ViewModelProvider.AndroidViewModelFactory(app) {override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return try {modelClass.getConstructor(String::class.java).newInstance(data)
        } ......
    }
}
  1. viewmodel 类定义
class CustomSimpleViewModel(private val data: String) : ViewModel() {fun print() {logEE(data)
    }
}
  1. 实例化 viewmodel
val model = ViewModelProvider(
                viewModelStore,
                CustomSimpleViewModelFactory(application, "自定义简略工厂创立 viewmodel")
            ).get(CustomSimpleViewModel::class.java)
            model.print()

1.5 应用委托机制创立 viewmodel(举荐)

屡次创立能够复用 model,不会从新创立

google 官网给咱们提供了 activity-ktx 库,通过它咱们能够应用委托机制创立 viewmodel

实现形式如下:

  1. 退出依赖
implementation 'androidx.activity:activity-ktx:1.2.2'
  1. 创立 viewmodel 类
class EnTrustModel : ViewModel() {fun print(){logEE("关注公众号 \" 安安安安卓 \"收费学常识")
    }
}
  1. 实例化 viewmodel
private val wtModel: EnTrustModel by viewModels<EnTrustModel> {ViewModelProvider.NewInstanceFactory()
    }
  1. 调用 viewmodel 的办法
 wtModel.print()

2、viewmodel 概览

ViewModel 类旨在以重视生命周期的形式存储和治理界面相干的数据。ViewModel 类让数据可在产生屏幕旋转等配置更改后持续留存。

咱们晓得 android 中的 Activity 和 Fragment 都是能够对生命周期进行治理的,如果 Activity/Fragment 被零碎销毁,例如屏幕旋转就必须从新创立 Activity/Fragment,这个时候就波及到一个数据恢复的问题。通常咱们会应用 onSaveInstanceState 和 onRestoreSaveInstance 两个办法对数据进行保留和复原,然而这两个办法只能解决数据量较小的状况。

如果是大数据量的状况那么用 viewmodel 就是不错的抉择了

同时 viewmodel 还能够帮忙咱们将业务逻辑从 Activity/Fragment 中分离出来

3、viewmodel 实现 Fragment 之间数据共享

同一个 activity 中呈现多个 Fragment,并且这些 Fragment 之间须要进行通信,这样的需要是很常见的

以往咱们可能会采纳下列形式实现数据共享:

  1. EventBus
  2. 接口回调
  3. 全局共享数据池

然而当咱们学了 viewmodel 后就会发现,viewmodel 能够轻松实现 Fragment 之间的数据共享

咱们能够让多个 Fragment 共用同一个 ViewModel 实现数据的共享。

本例中咱们要实现上面的性能:

activity 中有两个 fragment,FragmentA 和 FragmentB,咱们在 ViewModel 的构造方法中开始倒计时 2000s,在 FragmentA 中察看倒计时的数据并展现在页面上。而后再切换到 FragmentB,如果 FragmentB 中的倒计时的秒数没有重置为 2000,阐明咱们的数据共享胜利了。

上代码:

  1. 创立 ViewModel
class ShareDataModel: ViewModel() {val liveData = MutableLiveData<String>()
    var total = 2000L
    init {
        /**
         * 实现倒计时,一秒钟倒计时一次
         */
        val countDownTimer = object :CountDownTimer(1000 * total, 1000) {override fun onTick(millisUntilFinished: Long) {liveData.postValue("残余倒计时工夫 ${--total}")
            }
            override fun onFinish() {logEE("倒计时实现")
            }
        }
        countDownTimer.start()}
}
  1. 创立 FragmentA
class FragmentA : Fragment() {private val model: ShareDataModel by activityViewModels()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {return inflater.inflate(R.layout.fragment_a, container, false).apply {
            model.liveData.observe(viewLifecycleOwner,
                {value -> findViewById<TextView>(R.id.tv_aresult).text = value })
        }
    }
}
  1. 创立 FragmentB
class FragmentB : Fragment() {private val model: ShareDataModel by activityViewModels()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {return inflater.inflate(R.layout.fragment_b, container, false).apply {
            model.liveData.observe(viewLifecycleOwner,
                {value -> findViewById<TextView>(R.id.tv_bresult).text = value })
        }
    }
}
  1. 展现后果

通过 gif 咱们发现,即便 replace 后倒计时的数据依然没有扭转,阐明咱们胜利实现了数据共享

4、对于 viewmodel 的几个知识点

  1. viewmodel 相对不能够援用视图
  2. 在 activity 的存在期间可能会屡次走 onCreate 办法,然而咱们的 viewmodel 只有 actvity 完结并销毁的状况下才会被回收
  3. viewmodel 通过 lifecycle 来察看 activity/fragment 的生命周期

5、源码解析

5.1、创立 ViewModel 源码解析

先看一下创立 ViewModel 的代码:

val model = ViewModelProvider(
                viewModelStore,
                CustomSimpleViewModelFactory(application, "自定义简略工厂创立 viewmodel")
            ).get(CustomSimpleViewModel::class.java)

那么就从 ViewModelProvider 构造方法和 get 办法说起吧,

  1. ViewModelProvider 构造方法

ViewModelProvider 构造方法传入两个参数,ViewModelStore(用来存储管理 ViewModel)和 Factory(创立 ViewModel 的工厂)

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
  1. 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);
    }

getCanonicalName 办法能够获取一个能惟一标识 Class<T> 的字符串,最终会用作生成咱们 ViewModelStore 中存储 ViewModel 的 key。

获取 key 后咱们将 key 和 modelClass 做为参数调用重载 get 办法

  1. 重载 get 办法

办法的解读写在代码中了

 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {ViewModel viewModel = mViewModelStore.get(key);// 从 ViewModelStore 中依据 key 获取 ViewModel,如果有就会返回

        if (modelClass.isInstance(viewModel)) {// 判断获取的 ViewModel 是否是传入的 modelClass 类型
            if (mFactory instanceof OnRequeryFactory) {// 不看这里
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;// 如果是就返回 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);// 创立 ViewModel
        }
        mViewModelStore.put(key, viewModel);// 将 ViewModel 增加到 mViewModelStore 中
        return (T) viewModel;
    }

5.2、ViewModelStore 类解析

ViewModelStore 中有三个重要办法 get、put、clear, 并且保护了一个 HashMap<String,ViewModel>

  1. get
 final ViewModel get(String key) {return mMap.get(key);
    }
很简略就是依据 key 获取 ViewModel
  1. put
 final void put(String key, ViewModel viewModel) {ViewModel oldViewModel = mMap.put(key, viewModel);//oldViewModel 指的是被笼罩的 ViewModel
        if (oldViewModel != null) {oldViewModel.onCleared();
        }
    }

将一个 ViewModel 增加到 map 中,并且将被笼罩的 ViewModel 数据清理(因为同一个 activity 中只能存在一个同类型的 ViewModel)

  1. clear

下一节会讲

clear 办法的调用机会(重点)

先看一下 clear 办法

public final void clear() {for (ViewModel vm : mMap.values()) {vm.clear();
      }
      mMap.clear();}

clear 办法的调用地位在 ComponentActivity 的构造方法中,代码如下:

getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    // And clear the ViewModelStore
                    if (!isChangingConfigurations()) {// 当有配置信息存在的时候返回:true(屏幕旋转,后盾被杀死),当没有配置信息的时候返回:false(利用被被动杀死)。getViewModelStore().clear();
                    }
                }
                }
            });

豁然开朗,原来 ViewModel 通过 LifeCycle 察看 Activity 生命周期的形式来解决数据的清理操作

关注我的公众号 “安安安安卓” 收费学常识 粗体

正文完
 0