好了,讲了半天的理论真是枯燥的要死,让我们举个栗子吧。
以编写一个个人信息页面为例,由
和对应的布局文件
- UserProfileFragment.java
组成。那么为了让这个页面能够展示个人信息,我们需要两个数据:
- user_profile_layout.xml
和之前做法不同的是我们还要创建一个基于ViewModel的类
用于保存用户信息,而不是直接在Fragment中保存,道理已经在前面阐述过。什么是ViewModel?这里简单的做一个概念上的介绍:
- UserProfileViewModel
A ViewModel provides the data for a specific UI component, such as a fragment or activity, and handles the communication with the business part of data handling, such as calling other components to load the data or forwarding user modifications. The ViewModel does not know about the View and is not affected by configuration changes such as recreating an activity due to rotation.
即:ViewModel是为特定的UI(例如Activity/Fragment)提供数据的类,同时它也承担和数据相关的业务逻辑处理功能:比如根据uid请求网络获取完整的用户信息,或者将用户修改的头像上传到服务器。因为ViewModel是独立于View(例如Activity/Fragment)这一层的,所以并不会被View层的事件影响,比如Activity被回收或者屏幕旋转等并不会造成ViewModel的改变。
好像啰里八嗦的讲了一堆还是不太明白,让我们来理一理现在都有涉及到哪些东西。现在已经有三个文件了:
: 个人信息的布局文件
- user_profile.xml
:承载布局文件,显示用户信息的Fragment
- UserProfileFragment.java
:为UserProfileFragment提供数据,并且根据用户在Fragment上的操作提供相应业务逻辑的类
- UserProfileViewModel.java
emmm......还是不太明白呀,show me the code!!!(简单起见,user_profile.xml布局文件大家自行脑补吧)
先从最不熟悉的
开始(这里面省略了如何获取和处理用户信息的业务逻辑,详细的获取用户信息的逻辑稍后的文章会有介绍),代码如下:
- UserProfileViewModel
- public
- class
- UserProfileViewModel
- extends
- ViewModel
- {
- private
- String
- userId
- ;
- private
- User
- user
- ;
- //初始化传递uid进来
- public
- void
- init
- (
- String
- userId
- )
- {
- this
- .
- userId
- =
- userId
- ;
- }
- //提供完整的用户信息
- public
- User
- getUser
- ()
- {
- return
- user
- ;
- }
- }
:
- UserProfileFragment
- public
- class
- UserProfileFragment
- extends
- LifecycleFragment
- {
- private
- static
- final
- String
- UID_KEY
- =
- "uid"
- ;
- private
- UserProfileViewModel
- viewModel
- ;
- @Override
- public
- void
- onActivityCreated
- (
- @Nullable
- Bundle
- savedInstanceState
- )
- {
- super
- .
- onActivityCreated
- (
- savedInstanceState
- );
- //通过Arguments获取uid
- String
- userId
- =
- getArguments
- ().
- getString
- (
- UID_KEY
- );
- //创建ViewModel,不必太在意ViewModel的创建形式,这个之后会做详细的分析。现在只需要明白是在哪里生成的就行。
- viewModel
- =
- ViewModelProviders
- .
- of
- (
- this
- ).
- get
- (
- UserProfileViewModel
- .
- class
- );
- viewModel
- .
- init
- (
- userId
- );
- }
- @Override
- public
- View
- onCreateView
- (
- LayoutInflater
- inflater
- ,
- @Nullable
- ViewGroup
- container
- ,
- @Nullable
- Bundle
- savedInstanceState
- )
- {
- return
- inflater
- .
- inflate
- (
- R
- .
- layout
- .
- user_profile
- ,
- container
- ,
- false
- );
- }
- }
注意:这里面UserProfileFragment继承的是LifecycleFragment而不是Fragment,这是由于目前 Architecture Components 处于Alpha阶段。等LifeCycle API稳定之后,Android Support Library里的Fragment将会实现LifecycleOwner接口。(至于什么是LifecycleOwner,这里暂不做介绍,大概的意思是和感知组件生命周期相关。)截止到写这篇文章为止,support V4中的Fragment已经实现了LifecycleOwner,LifecycleFragment处于Deprecated状态,所以可以直接继承V4 包中的Fragment即可。
好了,现在已经熟悉了三块代码了,那么我们怎么将他们联系在一起呢?换句话说,如果我们在
获取到了用户信息,那么我们怎么通知Fragment去显示相应的字段呢?(写个接口?不行的!这么高级的事情不能这么干的)这时候LiveData就上场了。那么什么是LiveData呢?这里仍然是做一个简单的概念上的介绍:
- UserProfileViewModel
LiveData is an observable data holder. It lets the components in your app observe LiveData objects for changes without creating explicit and rigid dependency paths between them. LiveData also respects the lifecycle state of your app components (activities, fragments, services) and does the right thing to prevent object leaking so that your app does not consume more memory.
即:LiveData是一个包含可以被观察的数据载体。这么说又有点不好理解了,其实他就是基于观察者模式去做的。当LiveData的数据发生变化时,所有对这个LiveData变化感兴趣的类都会收到变化的更新。并且他们之间并没有类似于接口回调这样的明确的依赖关系。LiveData还能够感知组件(例如activities, fragments, services)的生命周期,防止内存泄漏。其实这和RxJava非常相似,但是LiveData能够在组件生命周期结束后自动阻断数据流的传播,防止产生空指针等意外。这个RxJava是不同的。
那么说的这么好,这个LiveData怎么用呢?其实非常简单,就是把
中
- UserProfileViewModel
方法的返回类型从
- getUser()
改成
- User
就好,这样Fragment就能在
- LiveData
- <
- User
- >
发生改变的时候及时被通知,LiveData更厉害的功能是,当对应的Fragment生命周期结束时,自动释放持有的引用。具体代码如下:
- User
- public
- class
- UserProfileViewModel
- extends
- ViewModel
- {
- ...
- //private User user;
- private
- LiveData
- <
- User
- >
- user
- ;
- public
- LiveData
- <
- User
- >
- getUser
- ()
- {
- return
- user
- ;
- }
- }
对应的Fragment中代码:
- @Override
- public
- void
- onActivityCreated
- (
- @Nullable
- Bundle
- savedInstanceState
- )
- {
- super
- .
- onActivityCreated
- (
- savedInstanceState
- );
- viewModel
- .
- getUser
- ().
- observe
- (
- this
- ,
- new
- Observer
- <
- User
- >()
- {
- @Override
- public
- void
- onChanged
- (
- @Nullable
- User
- user
- )
- {
- //update UI
- }
- });
- }
这样一旦
中的
- UserProfileViewModel
发生改变,那么Fragment中就在
- user
会收到相应的改变,并根据改变更新相应的UI。
- onChanged()
如果对比之前类似RxJava这样的基于观察者模式的库时,就会发现这里并没有在Fragment的
方法中对
- onStop()
解注册观察者。因为在LiveData中这是没有必要的,因为LiveData可以感知Fragment的生命周期状态,在收到
- UserProfileViewModel
开始后调用通知回调,并且在
- onStart()
后不再调用回调通知。当然在收到Fragment的
- onStop()
方法后还会自动的移除观察者。
- onDestroy()
从上面的代码可以看出我们并没有对 Configuration Changes(例如用户旋转屏幕)做任何处理。
会在收到Configuration Changes时自动保存数据。当新的Fragment(Configuration Changes 后新生成的Fragment)进入生命周期时,它会收到之前的ViewModel实例,并且会收到ViewModel中保存的数据。从这里不难看出,ViewModel的生命周期要比Views长,所以不能在ViewModel中持有Views的引用,否则会造成内存泄漏等问题。 ViewModel详细的生命周期稍后的文章会做详细的分析。
- ViewModel
对上面这段文字总结下就是,把数据放在ViewModel中就不再需要担心生命周期的问题,而且对如果这个数据是以LiveData的形式被传递出去,那么任何改动都会被实时的传递给UI层。
这里有个疑问那就是如果Activity在
后LiveData中数据发生改变,但是此时Activity对应的ViewModel是不会回调数据改变的。那么如果这时Activty重新回到界面
- onStop()
后,还会收到最新的数据吗?答案是:会的。
- onResume()
举个简单的应用就是修改头像,我们会跳到修改头像的Activity去修改头像,在修改成功后返回个人中心页面,这时候数据会自动刷新减少了很多繁杂的逻辑代码。
好了,大概看到这里已经对Android Architecture Components中LifeCycle部分有了初步的认识,大概知道了Lifecycles(UserProfileFragment)、LiveData(LiveData<User>)、 ViewModel(UserProfileViewModel)在什么情况下使用,以及一些使用的优点,这样其实已经久够了。对于这样一个框架,首先应该建立起一个整体的认识,然后再去填充细节的用法和技巧。
来源: https://juejin.im/entry/5a055bc8f265da430a500c77