升职加薪学习旅程 -- ViewModel+LiveData

这里使用简单的在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;
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容