记 对ViewModel浅显的认识

前言

	ViewModel旨在管理Activity和Fragment中的用户界面的数据,还可以用来这两者之间的通讯。
	与ViewModel相关联的Activity或者Fragment,该两者只要处于活动状态ViewModel就不会被销毁。
	(这里需要配合LiveData使用),综合了网上的内容加上自己整理而出的有错误请帮忙指出谢谢了。

引用ViewModel

记 对ViewModel浅显的认识
导入androix替换掉support包。

简单使用:

/*
新建User类
*/	
			public class User    {
public String name;
public int age;

public User(String name, int age){
    this.name = name;
    this.age = age;
}

@NonNull
@Override
public String toString() {
    return "UserInformation:"+"\n"
            + name + "\n"
            +age ;
}
}

/*
继承ViewModel
*/

public class UseViewModel extends ViewModel {
	public final MutableLiveData<User> data = new MutableLiveData<>();

    public UseViewModel(){
      //模拟加载数据
    data.postValue(new User(" 张三",20));
   }

   // 数据操作
   public void doSomething(){
   User user = data.getValue();
   if(user != null) {
       user.name = "李四";
       user.age = 25;
       data.setValue(user);
   }
   }
}

/*
在Activity中创建实例
*/

public class MainActivity extends FragmentActivity {
private TextView textView;

private Button button;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = findViewById(R.id.view_model);
    button = findViewById(R.id.view_button);

    //实例化
    final UseViewModel viewModel = ViewModelProviders.of(this).get(UseViewModel.class);

    viewModel.data.observe(this, new Observer<User>() {
        @Override
        public void onChanged(User user) {
            textView.setText(user.toString());
        }
    });

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewModel.doSomething();//改变数据
        }
    });
}
}

这时候点击一下按钮,TextView里的数据就从原来的:张三 20,变成李四25。然后我们在旋转屏幕就会发现原来的数据还存在没有发生改变,这就是ViewModel的厉害的地方,这就是不的不提到ViewModel的生命周期了。

记 对ViewModel浅显的认识

  • Activity与Fragment通信
    有了ViewModel Activity就能和Fragment共用一个ViewModel,从而是他们互相通信接着上面的继续:
public class Fragment extends androidx.fragment.app.Fragment {
    private TextView fragmentText;
    private Button  fragmentButton;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment,container,false);
        //Fragment中的两个控件
        fragmentText = view.findViewById(R.id.fragment_view);
        fragmentButton = view.findViewById(R.id.fragment_button);


        //获取实例
        final UseViewModel model = ViewModelProviders.of(this).get(UseViewModel.class);
        model.data.observe(this, new Observer<User>() {
            @Override
            public void onChanged(User user) {
                //TextView 监控 数值的变化
                fragmentText.setText(user.toString());

            }
        });

        /*
        改变值
         */

        fragmentButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                model.doSomething();
            }
        });


        return view;
    }
}

点击按钮就会发现Fragment中的数据跟Activity中的数据一样了,并且旋转屏幕数据依然不会消失。至于Activity之间的通信实现方式跟上面差不多。

ViewModel源码

//从这一句开始
UseViewModel viewModel = ViewModelProviders.of(this).get(UseViewModel.class);

首先看 ViewModelProviders.of(this)方法

//在Fragment中获取ViewModel实例的
  public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }


    
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

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


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

of()方法就是为了在不同布局中构建ViewModelProvider,而ViewModelProvider就是提供ViewModel的。
Factory 是ViewProvider的内部的一个接口,它有两个实现类 NewInstanceFactory和 AndroidViewModelFactory 。
NewInstanceFactory的源码

 public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

NewInstanceFactory是用来实例化那种没有参数的class,并且通过newInstance()方法获取实例的。

AndroidViewModelFactory源码

 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;
      
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }

AndroidViewModelFactory是用来实例化构造方法中可能会有参数的class,并且可能具有Context的,通过newInstance(mApplication)实例化,如果没有就还是用newInstance()方法获取实例 从return super.create(modelClass);这里可以看出来。
现在来看new ViewModelProvider(activity.getViewModelStore(), factory);,第一个参数会调用FragmentActvity的getViewModelStore()方法,其返回类型为ViewModelStore,这个类是用来存储ViewModel的。

  • getViewModelStore()的源码:
 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) {//如果没有就创建一个新的ViewModelStore
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
 NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();

重点是这一句,那么NonConfigurationInstances是什么呢?继续往下看

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
        FragmentManagerNonConfig fragments;
    }

这个是FragmentActivity的一个嵌套类,保存ViewModelStore的实例。

 NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
//获取最近一次横竖屏切换时保存下来的数据。

继续看getLastNonConfigurationInstance()方法,

//这是Activity.class里的方法
public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
    //返回任意类型任意可以是fragment也可以是activity

我们继续深入getLastNonConfigurationInstance() ,该方法出现在onCreat方法内:

 protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
            mViewModelStore = nc.viewModelStore;
        }
        . . . . . .
 }

原来在activity调用onCreate方法时就保存了ViewModelStore的实例了,然后放入到NonConfigurationInstances里面,再次调用onCreate()方法时则又恢复过来。从而使横竖屏切换的时候数据不会丢失。
ViewModelProviders.of(this).get(UseViewModel.class),中的get()方法

 public <T extends ViewModel> T get( Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        //这里我们会发现如果直接使用new ViewModel()创建实例就会直接报错。
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
...
public <T extends ViewModel> T get( String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
		//先取缓存
        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
		//没有缓存就创建然后put起来
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

总体上还是很清晰的,有缓存就用缓存,没缓存就重新构建。
ViewModelStore
上面很多代码中都有这个,它其实是一个很简单的保存通过HaspMap来保存ViewModel的类

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
	//通过key缓存ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
	//通过key获取实例
    final ViewModel get(String key) {
        return mMap.get(key);
    }

    /**
     *  清除内部储存,并不再使用ViewModel
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

那么什么时候调用clear()清除ViewModel呢?继续深入Clear()发现在FragmentActivity中的onDestory()方法中出现。

   protected void onDestroy() {
        super.onDestroy();

        if (mViewModelStore != null && !isChangingConfigurations()) {//isChangingConfigurations()直接返回false
            mViewModelStore.clear();
        }

        mFragments.dispatchDestroy();
    }
								不足之处还请不吝提出谢谢。