Jetpack学习笔记之ViewModel

概念Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据。也可以这样理解:它是介于View和Model之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离开,也能够保持通信。这样更符合“单一职责原则”,页面负责展示数据以及处理用户交互。官方注释:ViewModels usually expose this information via {@link LiveData} or Android DataBinding. You can also use any
摘要由CSDN通过智能技术生成

概念

Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据。也可以这样理解:它是介于View和Model之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离开,也能够保持通信。这样更符合“单一职责原则”,页面负责展示数据以及处理用户交互。

官方注释:

ViewModels usually expose this information via {@link LiveData} or Android Data
Binding. You can also use any observability construct from you favorite framework.

ViewModel's only responsibility is to manage the data for the UI. It should never access  your view hierarchy or hold a reference back to the Activity or the Fragment.

生命周期

说明 ViewModel 随着 Activity 状态的改变而经历的生命周期。

ViewModel独立于配置变化,这意味着屏幕旋转所导致的Activity重建,并不会影响ViewModel的生命周期。概括一句话:将视图与数据分离,并独立于Activity的重建。

使用

implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'

ViewModel是一个抽象类,其中只有一个onCleared()方法。当ViewModel不再被需要,即与之相关的Activity被销毁时,该方法会被系统调用。我们可以在该方法中执行一些资源释放相关的操作。注意:由于屏幕旋转而导致的Activity重建,并不会调用此方法。

public abstract class ViewModel {

    // Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();

    private volatile boolean mCleared = false;

    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        ......
        onCleared();
    }

......
}

示例:

public class MyViewModel extends ViewModel {

    private Timer mTimerTask;
    private int mCount;

    public MyViewModel() {
        Log.e("MyViewModel", "MyViewModel 创建了");
    }

    public void startTimer() {
        if (mTimerTask == null) {
            mTimerTask = new Timer();
        }
        mTimerTask.schedule(new TimerTask() {
            @Override
            public void run() {
                Log.e("MyViewModel", "mCount -> " + mCount++);
            }
        }, 1000, 1000);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        Log.e("MyViewModel", "onCleared");
        if (mTimerTask != null) {
            mTimerTask.cancel();
        }
    }
}

--------------------------------------

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("SecondActivity", "onCreate");
        MyViewModel mMyViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        mMyViewModel.startTimer();
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.e("SecondActivity", "屏幕旋转了");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("SecondActivity", "onDestroy");
    }
}

---------------------------------------
Log:
2020-08-09 11:24:48.856 10531-10531/com.json.jetpack E/SecondActivity: onCreate
2020-08-09 11:24:51.263 10531-10531/com.json.jetpack E/SecondActivity: 屏幕旋转了
2020-08-09 11:24:51.367 10531-10531/com.json.jetpack E/SecondActivity: onDestroy
2020-08-09 11:24:51.424 10531-10531/com.json.jetpack E/SecondActivity: onCreate
2020-08-09 11:24:53.990 10531-10531/com.json.jetpack E/SecondActivity: onDestroy

对应ViewModel的生命周期
2020-08-09 11:24:48.856 10531-10531/com.json.jetpack E/MyViewModel: MyViewModel 创建了
2020-08-09 11:24:49.859 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 0
2020-08-09 11:24:50.860 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 1
2020-08-09 11:24:51.860 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 2
2020-08-09 11:24:52.428 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 3
2020-08-09 11:24:52.861 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 4
2020-08-09 11:24:53.429 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 5
2020-08-09 11:24:53.861 10531-11230/com.json.jetpack E/MyViewModel: mCount -> 6
2020-08-09 11:24:53.990 10531-10531/com.json.jetpack E/MyViewModel: onCleared

两点:

  • ViewModel实例化的过程,是通过ViewModelProvider来完成的, ViewModelProvider会判断ViewModel是否存在,若存在则直接返回,否则,它会创建一个ViewModel
  • 上面示例Log验证了ViewModel的生命周期,即配置的更改不影响ViewModel,只有用户主动销毁了Activity,ViewModel的onCleared方法会被调用,ViewModel的生命周期结束;

ViewModel原理

理解ViewModel原理的关键是ViewModelStore,从上面的示例我们看到,ViewModel的实例化过程是通过ViewModelProvider来实现的。

        MyViewModel mMyViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        mMyViewModel.startTimer();

--------------------------------
    private final ViewModelStore mViewModelStore;

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), ...);
    }

// 上述ViewModelProvider的生成最终会调用下面的这个构造函数
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

结论:ViewModelProvider保存了当前页面对应的ViewModelStore, 并负责操作ViewModelStore。
---> 有点类似ThreadLocal与ThreadLocalMap的感觉

而生成ViewModelProvider对象的时候,我们传入了一个参数 - ViewModelStoreOwner,因为我们的Activity是CompatActivity的子类,而CompatActivity实现了ViewModelOwner接口:(PS: Fragment也默认实现了ViewModelOwner)

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

所以上面示例,我们传入了this。

可以看到实现ViewModelStoreOwner接口,只需实现一个方法,该方法返回ViewModelStore.

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

可以看到,ViewModelStore是理解原理的关键:

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

ViewModeStore的源码很简单,简单的概括就是一个HashMap,缓存我们的ViewModel。

从ViewModelStore的源码可以看出,ViewModel实际上是以HashMap<String , ViewModel>的形式被缓存了起来。ViewModel与页面之间没有直接的关联,它们通过ViewModeProvider进行关联。

当页面需要ViewModel时,会向ViewModelProvider索要,ViewModelProvider检查该Model是否已经存在于缓存中了,若存在,则直接返回,若不存在,则实例化一个。

- ViewModelProvider

    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");
        }
// Key的生成规则:【DEFAULT_KEY  :canonicalName】
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

// 若存在,直接返回
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
// 若不存在,利用反射创建ViewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
// 创建完,缓存到ViewModelStore中
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

因此,Activity由于配置变化导致的销毁重建并不会影响ViewModel,看下ComponentActivity的构造函数中的源码就立刻明白了:

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

- ViewModelProvider
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }

- ViewModel
    @MainThread
    final void clear() {
        ......
        onCleared();
    }

注意:不要向ViewModel中传入任何类型的Context或带有Context引用的对象,这可能导致页面无法被销毁,从而引发内存泄露。

ViewModel与AndroidViewModel

但是如果希望在ViewModel中使用Context怎么办?可以使用AndroidViewModel,它继承自ViewModel,并接收Application作为Context。这意味着,它的生命周期和Application是一样的。

ViewModel与onSaveInstanceState()方法

onSaveInstance:只能保存少量的,能支持序列化的数据,可以持久化页面的数据,即当页面被彻底销毁时,数据还存在。
ViewModel:能保存页面中所有的数据,但是ViewModel不支持数据的持久化,当界面被彻底销毁的时候,ViewModel及其持有的数据就不存在了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Jetpack是Google提供的一套用于加速Android应用开发的工具包,其中包括了许多架构组件,其中之一就是ViewModelViewModel是一种设计模式,用于保存和管理与UI相关的数据。在传统的Android开发中,当屏幕旋转或者因为其他原因导致Activity或Fragment重建时,之前保存的临时数据就会丢失。而ViewModel的出现解决了这个问题。 ViewModel的主要作用是将数据与UI组件分离。它的工作方式是创建一个ViewModel类,并在其中保存需要与UI组件交互的数据。这样,当屏幕旋转或重建时,ViewModel实例不会销毁,数据也会得到保留。然后,在Activity或Fragment中,通过获取ViewModel实例,可以轻松地访问这些数据。 使用ViewModel的好处有很多。首先,它可以避免内存泄漏,因为ViewModel的生命周期与Activity或Fragment无关。其次,它可以节省资源,因为当Activity或Fragment销毁时,ViewModel实例可以被系统缓存起来,下次再创建时可以直接返回该实例。另外,由于ViewModel保存了与UI相关的数据,可以减少因为屏幕旋转导致的数据重复加载的问题。 在使用ViewModel时,你可以选择使用Android Jetpack中的其他架构组件来进一步提高开发效率,比如通过LiveData实现数据的观察和通知,或者通过DataBinding来实现UI与数据的自动绑定。 总之,ViewModelAndroid Jetpack中非常重要的一个架构组件,它的出现实现了数据与UI的解耦,提高了开发效率,并且解决了数据丢失的问题。希望通过这篇文档的详解,你对ViewModel有了更深入的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值