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类,监听就是通过它实现

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

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

  • 相关阅读:
    Windbg Call Stack(调用堆栈)窗口的使用
    设置微软符号服务器的又一方法
    windbg是如何搜索符号文件的?
    如何关闭/禁用.NET JIT调试对话框
    EXCEPTION_HIJACK(0xe0434f4e)异常的抛出过程
    异常EXCEPTION_HIJACK(0xe0434f4e)
    Windows WoW64浅析
    在执行一行代码之前CLR做的68件事
    异常CLRDBG_NOTIFICATION_EXCEPTION_CODE( 0x04242420)的抛出过程
    异常CLRDBG_NOTIFICATION_EXCEPTION_CODE( 0x04242420)
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/14121108.html
Copyright © 2011-2022 走看看