这里使用简单的在Activity中创建一个ViewModel,然后viewModel中有一个LiveData用于改变fragment中的显示
class CollaborateViewModel : ViewModel() {
private val collaborateData = MutableLiveData<String>()
fun setCollaborateData(value: String) {
this.collaborateData.value = value
}
fun postCollaborateData(value: String) {
this.collaborateData.postValue(value)
}
fun getCollaborateData(): MutableLiveData<String> {
return collaborateData
}
}
在Activity中创建ViewModel,并给出Post,Set Value的方法供Fragment使用
这里使用activity-ktx依赖中的viewModel创建
class ViewModelCollaborateActivity : AppCompatActivity() {
private lateinit var collaborateActivityBinding : ActivityViewModelCollaborateBinding
//如果没有引入androidx.activity:activity-ktx:x.x.x,fragment则是androidx.fragment:fragment-ktx:x.x.x
//private lateinit var collaborateViewModel :CollaborateViewModel
//collaborateViewModel = ViewModelProvider(this).get(CollaborateViewModel::class.java)
//引入ktx依赖后
private val collaborateViewModel :CollaborateViewModel by viewModels()
private var valueCount : Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
collaborateActivityBinding = ActivityViewModelCollaborateBinding.inflate(LayoutInflater.from(this))
setContentView(collaborateActivityBinding.root)
//添加fragment
val translation = supportFragmentManager.beginTransaction()
translation.replace(R.id.layout_container, ViewModelCollaborateFragment())
translation.commit()
}
fun setCountValue(){
collaborateViewModel.setCollaborateData("Current Count = ${++valueCount}")
}
fun postCountValue(){
collaborateViewModel.postCollaborateData("Current Count = ${++valueCount}")
}
fun resetCount(){
valueCount = 0
collaborateViewModel.setCollaborateData("Current Count = $valueCount")
}
}
在Fragment中,使用与Activity协同使用一个ViewModel,使用不同的事件修改LiveData的值,区别于Post与Set方法的不同
1.Post是使用一个线程修改Value,然后通过Handler去Post这个值的修改,所以处理的时间会比直接Set慢,并且如果快速多次的设置Post方法,只会将最后一个值给提交出去,但是Set不会,set的每次修改都会被监听到
class ViewModelCollaborateFragment :Fragment() {
private lateinit var collaborateBinding: FragmentViewModelCollaborateBinding
//添加fragment-ktx时使用,否则使用 collaborateViewModel = ViewModelProvider(requireActivity())[CollaborateViewModel::class.java]
private val collaborateViewModel : CollaborateViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
collaborateBinding = FragmentViewModelCollaborateBinding.inflate(LayoutInflater.from(context),container,false)
return collaborateBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
collaborateViewModel.getCollaborateData().observe(viewLifecycleOwner) {
Log.e("SetValue", "onViewCreated: $it", )
collaborateBinding.tvViewModelText.text = it
}
collaborateBinding.tvSetValue.setOnClickListener { parentSetValue() }
collaborateBinding.tvPostValue.setOnClickListener { parentPostValue() }
collaborateBinding.tvPostAndSetValue.setOnClickListener {
parentPostValue()
parentSetValue()
}
collaborateBinding.tvSetValueCycle.setOnClickListener {
for (index in 0 .. 100){
parentSetValue()
}
}
collaborateBinding.tvPostValueCycle.setOnClickListener {
for (index in 0 .. 100){
parentPostValue()
}
}
collaborateBinding.tvFirstSetAndCyclePostValue.setOnClickListener{
parentSetValue()
for (index in 0 .. 100){
parentPostValue()
}
}
collaborateBinding.tvResetCount.setOnClickListener {
resetCount()
}
}
fun parentSetValue(){
if (activity is ViewModelCollaborateActivity){
(activity as ViewModelCollaborateActivity).setCountValue()
}
}
fun parentPostValue(){
if (activity is ViewModelCollaborateActivity){
(activity as ViewModelCollaborateActivity).postCountValue()
}
}
fun resetCount(){
if (activity is ViewModelCollaborateActivity){
(activity as ViewModelCollaborateActivity).resetCount()
}
}
}
在这里一个Activity与Fragment的协同使用ViewModel的简单样例就完成了
在Activity中,ViewModel的生命周期是贯彻Activity的生命周期的,并且如果Activity旋转屏幕这样的重新显示,Activity还是可以通过ViewModel直接获取到之前的数据
首先先知道ViewModelStore,在这里面使用一个HashMap<String,ViewModel> 这样类型的一个map来存储ViewModel,这里的Key是Class<T>.canonicalName值,是通过传入的Class生成的一个名称来保存
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
在Activity中,随着Lifecycle的改变,如果页面生命周期是ON_DESTROY,且是被旋转屏幕时,此时ViewModelStory不会去清除掉之前生成的ViewModel,这样就为之后的页面重新生成之后重新获取到ViewModel中的数据打下铺垫
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()) {
getViewModelStore().clear();
}
}
}
});
然而保存ViewModel是在ComponentActivity中,如果viewmodel为空时,就从上次保存的NonConfigurationInstances对象中获取,如果都为空,择创建一个新的,否则就返回已有ViewModel
@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.");
}
ensureViewModelStore();
return mViewModelStore;
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
然而Activity是从哪里保存ViewModel到NonConfigurationInstances对象中呢,在onRetainNonConfigurationInstance这个方法中,获取以及保存返回
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;
}