侧边栏壁纸
博主头像
慢行的骑兵博主等级

贪多嚼不烂,欲速则不达

  • 累计撰写 29 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Jetpack组件-三-ViewModel

慢行的骑兵
2021-09-13 / 0 评论 / 0 点赞 / 76 阅读 / 1,210 字

一.简介

  • 官网
  • ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,ViewModel中数据会一直存活即使 activity configuration发生变化,比如横竖屏切换的时候。
  • 优点
    • 数据持久化
    • 异步回调问题,不会造成内存泄漏
    • 在Mvvm架构中隔离Model层与View层
    • 可实现Fragments间共享数据

二.基本使用

  • ViewModel环境:implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
  • 基本使用方面是略写,使用方面的细节大家可以参考其它资料,本篇笔记以源码分析为主。

2.1.自定义ViewModel的实现类

public class MyViewModel extends ViewModel {
    private MutableLiveData<String> number;
    private MutableLiveData<User> user;

    public MutableLiveData<String> getNumber(){
        if(number == null){
            number = new MutableLiveData<>();
            number.setValue("");
        }
        return number;
    }

    public MutableLiveData<User> getUser(){
        if(user == null){
            user = new MutableLiveData<>();
        }
        return user;
    }

    public void getUserFromNet(){
        //获取数据
    }
}

2.2.在MainActivity中使用

public class MainActivity extends AppCompatActivity {
    MyViewModel myViewModel;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);
        //ViewModel的创建不能直接去new
        //当我们执行这句代码的时候,会去ViewModelStore获取MyViewModel对象,如果没有就创建一个
        //问题1:为什么通过一下方式进行创建,得到的ViewModel会是同一个
        myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        textView.setText( myViewModel.getNumber().getValue()+"");
    }

    public void changeData(View view) {
        myViewModel.getNumber().setValue(myViewModel.getNumber().getValue()+1);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    public void changeData2(View view) {
        myViewModel.getNumber().setValue(myViewModel.getNumber().getValue()+1);
    }
}

三.源码分析

  • 我们从ViewModel的创建,作为源码分析的入口

3.1.分析ViewModelProvider的构造方法

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    //4.将ComponentActivity中的成员变量mViewModelStore赋值给ViewModelProvider中的成员变量mViewModelStore
    //1.继续调用其它构造方法
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    //2.将store(activity)赋值给mViewModelStore,在3.2中分析ViewModelStore
    mViewModelStore = store;
}

3.2.ViewModelStore

public class ViewModelStore {

    //保存ViewModel的map
    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());
    }

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
  • 当activity的生命周期结束了,ViewModelStore依然存在。在2.2中,当ViewModle被创建出来了,就会将ViewModel放到ViewModelStore中。当再次去get的时候,会检查ViewModelStore中的map是否存在,存在直接获取,不存在才创建。下面分析一下get方法源码
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    //拿到包名+类名
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //3.现根据key去获取,获取到了就直接返回,未获取到才创建
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);	//4.重新创建
    } else {
        viewModel = (mFactory).create(modelClass);	
    }
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}
  • MainAvtivity的生命周期其实和ViewModel的生命周期是没有关联的。

  • 问题2:ViewModelStore是怎么实现不被销毁的

  • 注意:sdk 30跟28存在着很大的区别,ViewModelStore的管理是存在着很大的差异,28是通过fragment来管理的,现在是把ViewModelStore写成了一个共有数据;

  • 此时我们再回到步骤1,分析owner.getViewModelStore();

  • 问题3:activity关闭了,ViewModel依然存在,其原因是什么?分析步骤4

3.3.(mFactory).create(modelClass)

public interface Factory {
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

//找实现类-AbstractSavedStateViewModelFactory
public final <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
    //临时保存数据的(内存中),当后台进程关闭,数据也能得到保留
    //ViewModel是存在SavedStateHandleController中的(重点-需要记忆)
    SavedStateHandleController controller = SavedStateHandleController.create(
            mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
    T viewmodel = create(key, modelClass, controller.getHandle());
    viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
    return viewmodel;
}
  • 问题2和问题3其实是同一类问题,3.3中的注释有解释;

3.4.ViewModel销毁的时机

  • 在activity的父类ComponentActivity中的构造方法中,利用了Lifecycle,当activity的生命周期方法触发,并且!isChangingConfigurations()为true,ViewModel就会被销毁;
public ComponentActivity() {
    //...
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });

    //...
}

3.5.总结

  • 把上面提及到的三个问题弄清楚了,ViewModle源码相关也差不多了。
0

评论区