Android Architecture Components 系列(四)ViewModel

带着下面的这个问题开始ViewModel的学习:
    ViewModel的生命周期是如何控制的,而且如何保证在必定范围内的惟一性?
 
官方文档里这样写到:
    The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
    ViewModel 简单来讲 这个类是设计用来存储UI层的数据,以及管理对应的数据,而且这些数据不受配置变化的影响。可以作到当数据修改的时候,能够立刻刷新Ui效果,好比屏幕的旋转操做。
引言
    Android系统自己提供控件,好比Activity 和Fragment ,这些控件都是具备生命周期方法,这些生命周期方法被系统调用。
  •      activity or Fragment 不适于保存大量数据
    可是当这些控件由于一些缘由被系统随时销毁或是从新建立时候,任何存放在这里的数据都有可能会丢失。举个栗子,Activity中有一个查询获得的用户列表,这时候Activity被重建,新的Activity须要再次去获取用户数据。若是简单的数据可使用控件自带的方法,将数据保存到onSaveInstances()方法中,在下次OnCreate()中从新将数据取出来,好比UI状态这类少许数据是能够的,可是对于上述提到的大量的数据,好比列表数据,这样作就很不合时宜了。
  •    在Activity中进行大量的耗时操做和数据的回调管理会形成大量的资源浪费
    另外一个问题,常常须要在Activty中加载数据,这些数据通常是异步耗时操做,由于获取数据须要联网或是花费很长时间。当前的Activity就须要管理这些数据调用,不然可能产生内存泄露的问题。这些回调事件可能会很是耗时,这时候Ui组件管理这些调用的同时,在UI组件销毁时候还须要清除这些调用。这就形成须要花费更多成本进行维护管理,并且在UI重建时候如configuration change,又须要再次从新调用,形成了不少资源的浪费。
  •     Activity的代码臃肿形成了维护和测试的不友好
    同时Ui组件不只仅只是用来加载数据,更要对用户的操做做出响应和处理,还要加载其余资源,致使Ui类变的愈来愈大,愈来愈臃肿,这就是常说的上帝类。这种状况对代码的维护和 测试 都是很是不友好的。
    前人在这些问题的基础上开发出了MVP框架 ,建立相同相似于生命周期函数作代理,一方面减小Activity的代码量,一方面优化了各个功能模块的逻辑。
    
ViewModel
Google官方提出的AAC 的ViewModel 就是用于解决上述问题。
    ViewModel 用于为Ui组件提供管理数据,而且可以在须要的时候扔能保持里面的数据。其提供的自动绑定的形式,当数据源有更新的时候能够自动当即的更新Ui效果。
下面看一个官方的小代码实例:
    publicclass MyViewModel extends ViewModel { 
    privateMutableLiveData<List<User>> users; 
    publicLiveData<List<User>>getUsers() { 
        if(users ==null) { 
        users =newMutableLiveData<List<Users>>(); 
            loadUsers(); 
        } 
        returnusers; 
    } 
        privatevoidloadUsers() { 
        // do async operation to fetch users 
        } 
   }
You can then access the list from an activity as follows:
    Activity 访问User List 数据 
publicclass MyActivity extends AppCompatActivity { 
    publicvoidonCreate(Bundle savedInstanceState) { 
        MyViewModel model = ViewModelProviders.of( this).get(MyViewModel.class); 
        model.getUsers().observe(this, users -> { 
            // update UI 
         }); 
       }
         @Override
        protected void onDestroy() {
            super.onDestroy();
            mViewModelStore.clear()
        }
  }
      当咱们获取ViewModel实例的时候,ViewModel 对象是经过ViewModelProvider保存在LifeCycle中,ViewModel会一直保存在LifeCycle中,直到Activity或是Fragment被销毁掉,Framework会调用ViewModelStore的clear方法,也就是调用ViewModel的onCleared()方法来进行资源的清理,那么ViewModel 也会被销毁的。
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
ps:由于ViewModel的生命周期是和Activity分开的,因此在ViewModel中禁止引用任何View对象或者任何引用了Activity的Context的实例对象。若是ViewModel中须要Application的context能够继承AndroidViewModel类。
那么思考 用户主动按了返回Home键,主动销毁了这个Activity呢?
这时候系统会调用ViewModel的onClear()方法 清除ViewModel中的数据。
    先上一张 ViewModel的生命周期示意图:
    如图 ,VIewModel相对于Activity 或是Fragment 的生命周期来讲很是简单,就一个生命周期函数:onCleared(),会在Activity的onDestroy()以后执行,那么是否是能够说在Fragment的生命周期函数内也是在onDestroy以后执行呢?
ViewModel的实现过程
    //获取当前类的ViewModel提供者,以后在传入须要得到的ViewModel的类型
 MyViewModel model = ViewModelProviders.of(this) .get(MyViewModel.class); 
 
    解析:若是传入的是this 是Fragment 就先判断是否已经关联到Activity上,没有就抛出非法参数异常。以后在初始化一个sDefaultFactory对象,用于建立ViewModelProvider,并在viewModelProvider的构造函数中初始化一个ViewModelStores对象
 
 
俩个工厂方法用于建立ViewModelStore ,并区分传入的是Activity 仍是 Fragment
以传入的是Activity为例:
    建立FragmentManager对象,并获取,查找当前的Activity有没有添加过HoldFragment, 若是没有呢则去尚未添加的Activity/Fragment 的 HoldFragment列表中查询,看看有没有已经建立的HoldFragment。若是没有就建立一个新的HoldFragment ,同时给Application注册一个Activity的生命监听器,再把建立饿的HoldFragment添加到缓存列表中。
HoldFragment()又是如何操做的呢?
 
   在onCreate方法中执行了将在未添加到Activity或是Fragment的HolderFragment列表中删除当前的Activity 或是Fragment。
    在onDestroy方法中执行了ViewModel的clear方法,当Ui组件被销毁的时候自动通知Lifecycle进行解除绑定清除ViewModel资源的操做。
     简单总结以上内容:
 
  • 查找当前的Activity/Fragment中是否有已经添加的HoldFragment,有则返回。
  • 查找当前的Activity/Fragment是否有已经建立可是并未添加的HoldFragment,有则返回。
  • 注册Activity/Fragment的生命周期监听。
  • 建立新的HoldeFragment,并添加的缓存列表。
  • HoldFragment在关联到Activity/Fragment以后会在缓存中去掉当前的Activity/Fragment对应的HoldFragment
  • HoldFragment在onDestory的时候会调用其成员变量mViewStore的clear方法。
回到以前建立ViewModelProvider的地方:
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store   {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
*                new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStorestore, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore= store;
}
    构造方法中先给两个成员变量赋值,而后经过ViewModelStore的get方法获取ViewModel对象
        viewModel = mFactory .create(modelClass);
        mViewModelStore.put(key,viewModel);
    若是获取不到传入类的ViewModel 就经过工厂类Factory建立一个新的viewModel 经过put方法添加到ViewModelStore中。
简而言之就是利用Fragment的方式去获取生命周期,而后再利用工厂类建立ViewModel。
关于在必定范围内的惟一性,由于ViewModelStore是HoldFragment的成员变量,HoldFragment是经过FragmentManager添加到指定的Activity/Fragment,那么对于当前的宿主,只有一个HoldFragment,也就只有一个ViewModelStore,同时也就只有一个ViewModel。
 
ViewModel的在Fragment间的数据分享
     有时候一个Activity中的两个或多个Fragment须要分享数据或者相互通讯,这样就会带来不少问题,好比数据获取,相互肯定生命周期。
        ViewModel能够很好的解决该类问题。有两个Fragment,一个Fragment提供点击每一个item显示的详情,另外一个Fragment提供一个列表。那两个的交互代码应该是如何表现的呢?
实例代码以下:
//ViewModel
public class SharedViewModel extends ViewModel { 
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); 
    
    public void select(Item item) { 
        selected.setValue(item);
     } 
    public LiveData<Item> getSelected() { 
        return selected; 
    } 
//第一个Fragment
public class MasterFragment extends Fragment { 
    private SharedViewModel model; 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
         model = ViewModelProviders.of(getActivity())
                                        .get(SharedViewModel.class);           
          itemSelector.setOnClickListener(item -> { 
                        model.select(item); 
                    }); 
        } 
   } 
    //第二个Fragment
public class DetailFragment extends LifecycleFragment { 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        SharedViewModel model = ViewModelProviders.of(getActivity())
            .get(SharedViewModel.class); 
        model.getSelected().observe(this, { 
                    item -> // update UI 
            }); 
        }
    }
两个Fragment都是经过getActivity()来获取 ViewModelProvider。这意味着两个Activity都是获取的属于同一个Activity的同一个ShareViewModel实例。
这样作优势以下:
  • Activity不须要写任何额外的代码,也不须要关心Fragment之间的通讯。
  • Fragment不须要处理除SharedViewModel之外其余的代码。这两个Fragment不须要知道对方是否存在。
  • Fragment的生命周期不会相互影响,即便用其余Fragment替换其中的一个Fragment,另外一个依然能也不受影响。
 

ViewModel和SavedInstanceState对比

    最后前文提到保存简单的数据可使用Activity自带的SavedInstanceState方法,那这个和viewMOdel的区别是?
    ViewModel使得在屏幕旋转等操做时候保存数据变得很便捷,可是这不能用于应用被系统kill时的持久化数据。举个简单的例子,有一个界面展现国家信息。不该该把整个国家信息放到SavedInstanceState里,而是把国家对应的id放到SavedInstanceState,等到界面恢复时,再经过id去获取详细的信息。这些详细的信息应该被存放在数据库中。说到数据库,下篇文章将会介绍Android Architecture Components提供的Room来操做数据库。
 
小结
    ViewModel其实就是经过给宿主添加Fragment的方式来获取宿主的生命周期。在HoldFragment中持有一个集合用于保存当前宿主的ViewModel,只须要在onDestroy方法中调用集合的clear方法,就能间接调用到ViewModel的onCleared方法了,这样实现了对其生命周期的控制。
 
相关文章
相关标签/搜索