zoukankan      html  css  js  c++  java
  • ViewModel浅析

    先写一个demo:通过ViewMOdel实现fragment之间的通信

     布局:

    【activity_main】
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <fragment
            android:id="@+id/master_fragment"
            android:name="com.example.myapplication.MasterFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    
        <fragment
            android:id="@+id/detail_fragment"
            android:name="com.example.myapplication.DetailFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    </LinearLayout>
    
    【fragment_detail】
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </RelativeLayout>
    
    【fragment_master】
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮" />
    </RelativeLayout> 

    代码:

    package com.example.myapplication;
    
    import android.content.ClipData.Item;
    
    import androidx.lifecycle.LiveData;
    import androidx.lifecycle.MutableLiveData;
    import androidx.lifecycle.ViewModel;
    
    public class SharedViewModel extends ViewModel {
        private final MutableLiveData<String> selected = new MutableLiveData<String>();
    
        public void select(String item) {
            selected.setValue(item);
        }
    
        public LiveData<String> getSelected() {
            return selected;
        }
    }
    
    
    package com.example.myapplication;
    
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.lifecycle.Observer;
    import androidx.lifecycle.ViewModelProviders;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    
    package com.example.myapplication;
    
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.lifecycle.ViewModelProviders;
    
    public class MasterFragment extends Fragment {
        private SharedViewModel model;
        private TextView button;
        int num = 0;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_master, null);
            button = view.findViewById(R.id.button);
            return view;
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    model.select("数字:" + (num++));
                }
            });
    
        }
    }
    
    package com.example.myapplication;
    
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.Fragment;
    import androidx.lifecycle.Observer;
    import androidx.lifecycle.ViewModelProviders;
    
    public class DetailFragment extends Fragment {
        private SharedViewModel model;
        private TextView text;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_detail, null);
            text = view.findViewById(R.id.text);
            return view;
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(getViewLifecycleOwner(), new Observer<String>() {
                @Override
                public void onChanged(String str) {
                    text.setText(str);
                }
            });
        }
    }

    这样通过点击MasterFragment的按钮就能控制DetailFragment的文本了。其实SharedViewModel就是一个中转站,一个仓库,一个存一个取。因为很多通信其实都是通过底层存储来实现的

    ViewModel大部分都用来实现MVVM模型,M层用来操作数据,V层负责展示界面,VM层用来逻辑处理。

    下面我们来看源码:

    ViewModelProviders.of(getActivity())

    of方法

    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

    当factory为null,会根据application创建一个factory,这样保证了一个应用使用的是同一个factory

    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.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

    getViewModelStore()方法新建了一个ViewModelStore,ViewModelStore类很简单,底层用的是HashMap,这时我们就可以知道了,ViewModel就是根据activity的名字存取Map

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

    这里有put有get,一开始的ViewModelProviders.of(getActivity()).get(SharedViewModel.class)的get先是跳到ViewModelProvider的get方法,然后果不其然跳到了ViewModelStore的get方法

    接下来我们来看是怎么触发数据变化的,先看接收方

    model.getSelected().observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String str) {
            text.setText(str);
        }
    });

    observe实际上就是增加一个监听,那我们只要找到什么时候调用了onChanged方法就行了

    model.select("数字:" + (num++));

    selected.setValue(item);

    进到LiveData里

    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

    dispatchingValue方法里调用了一个considerNotify方法

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

    这里调用了onChanged,终于完美闭环。

    ViewModel基本都会跟LiveData结合使用,LiveData里面有个ObserverWrapper类,监听就是通过它实现

    其实代码功能底层基本都是通过存储来实现的,无论是线程间通信还是进程间通信,一存一取,就通信了。还有监听,观察者模式的实时动态变化基本离不开监听。

     欢迎关注我的微信公众号:安卓圈

  • 相关阅读:
    web前端的发展态势
    AngularJs 简单入门
    css代码优化篇
    git提交报错:Please make sure you have the correct access rights and the repository exists.
    Activiti工作流框架学习
    遍历map集合的4种方法
    js设置日期、月份增加减少
    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    webservice_rest接口_学习笔记
    相互匹配两个list集合+动态匹配${}参数
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/14121108.html
Copyright © 2011-2022 走看看