谷歌在2018年的I/O大会上推出了Jetpack的概念,意图在于统一框架与UI组件。
Android 框架可以管理界面控制器(如 Activity 和 Fragment)的生命周期。Android 框架可能会决定销毁或重新创建界面控制器,以响应完全不受您控制的某些用户操作或设备事件。
如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState()
方法从 onCreate()
中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合潜在的大量数据,如用户列表或位图。
另一个问题是,界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。
诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信,如权限请求。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。
从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效
图1
ViewModel 对象存在的时间范围是获取 ViewModel
时传递给 ViewModelProvider
的 Lifecycle。ViewModel
将一直留在内存中,直到限定其存在时间范围的 Lifecycle
永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
图 1 说明了 Activity 经历屏幕旋转而后结束时所处的各种生命周期状态。该图还在关联的 Activity 生命周期的旁边显示了 ViewModel
的生命周期。此图表说明了 Activity 的各种状态。这些基本状态同样适用于 Fragment 的生命周期。
您通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel
系统可能会在 Activity 的整个生命周期内多次调用 onCreate()
,如在旋转设备屏幕时。ViewModel
存在的时间范围是从您首次请求 ViewModel
直到 Activity 完成并销毁
ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是由ViewModelProvider实现的。
ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到不能在ViewModel中持有Activity的引用。
ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
-ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData> users;
public LiveData> getUsers() {
if (users == null) {
users = new MutableLiveData>();
loadUsers();
}
return users;
}
private void loadUsers() { }}
-Activity
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
//更新UI
});
}}
如果重新创建了该 Activity,它接收的 MyViewModel
实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 ViewModel
对象的 onCleared()
方法,以便它可以清理资源。
一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯
代码示例:
创建ViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData selected = new MutableLiveData();
public void select(User mUser) {
selected.setValue(mUser);
}
public LiveData getSelected() {
return selected;
}
}
发送数据的Fragment
public class MasterFragment extends Fragment implements View.OnClickListener {
private SharedViewModel model;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
}
@Override
public void onClick(View v) {//模仿点击事件去刷新数据
model.select(new User());
}
}
接收数据并刷新UI的fragment
public class DetailFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//获取ViewModel
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, new Observer() {
@Override
public void onChanged(@Nullable User user) {
//刷新UI
}
});
}
}
上面两个Fragment都用到了如下代码来获取ViewModel,getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。
ViewModel总结
1.ViewModel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。
2.ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
3.ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
4.ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。
小广告
这里创建ViewModel 使用的是自己简单封装的一个注解编译器 用来解放双手生成对应的ViewModel的
示例代码
import java.util.List;
@ViewModel
public class My {
@LiveData
List users;
}
在app build下 generated source apt 包下会生成对应的ViewModel
生成代码如下:
public class MyViewModel extends ViewModel {
private MutableLiveData> mUsers = new MutableLiveData();
public void postUsersValue(List mUsers) {
this.mUsers.postValue(mUsers);
}
public MutableLiveData> getUsersLiveData() {
return this.mUsers;
}
public void setUsersValue(List mUsers) {
this.mUsers.setValue(mUsers);
}
public List getUsersValue() {
return this.mUsers.getValue();
}
public MyViewModel getMyViewModel() {
return this;
}
}
插件依赖
annotationProcessor 'com.ssl.generate:generateViewModel:1.2.9' //注解编译器
implementation 'com.ssl.annotation:annotation:1.2.7'//注解
LiveData
是一种可观察的数据容器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer
类表示)的生命周期处于 STARTED
或 RESUMED
状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 仅将更新通知给活跃观察者。注册观察 LiveData
对象的非活跃观察者不会收到更改通知。
您可以注册与实现 LifecycleOwner
接口的对象配对的观察者。有了这种关系,当相应 Lifecycle
对象的状态更改为 DESTROYED
时,便可移除此观察者。这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData
对象而不必担心泄漏 - 当 Activity 和 Fragment 的生命周期被销毁时,会立即取消订阅它们。
LiveData的优势
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer
对象。您可以整合代码以在这些 Observer
对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。
不会发生内存泄漏
观察者绑定到 Lifecycle
对象,并且在其关联的生命周期被销毁后,会自我清理。
不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity ),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
您可以使用单一实例模式扩展 LiveData
对象以封装系统服务,以便可以在应用中共享它们。LiveData
对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData
对象。
创建 LiveData
实例以存储某种类型的数据。这通常在 ViewModel
类中完成。
创建 Observer
对象来定义 onChanged()
方法,该方法可以控制当 LiveData
对象存储的数据更改时会发生什么。通常在界面控制器(如 Activity 或 Fragment)中创建 Observer
对象。
使用 observe()
方法将 Observer
对象附加到 LiveData
对象。observe()
方法采用 LifecycleOwner
对象。这样会使 Observer
对象订阅 LiveData
对象,以使其收到有关更改的通知。通常在界面控制器(如 Activity 或 Fragment)中附加 Observer
对象。
代码示例:
创建LiveData
LiveData 是一个封装容器,可以用于任何数据,包括实现 Collections
的对象,如 List
。LiveData
对象通常存储在 ViewModel
对象中
public class NameViewModel extends ViewModel {
private MutableLiveData
public MutableLiveData
if (mCurrentName == null) {
mCurrentName = new MutableLiveData
}
return mCurrentName;
}}
LiveData - 观察对象
LiveData 仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。此行为的一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。
public class NameActivity extends AppCompatActivity {
private NameViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取当前ViewModel对象
mModel = ViewModelProviders.of(this).get(NameViewModel.class);
//创建观察者
final Observer
@Override
public void onChanged(@Nullable final String newName) {
//改变数据
mNameTextView.setText(newName);
}
};
//注册观察者
mModel.getCurrentName().observe(this, nameObserver);
}}
LiveData - 更新数据
设置观察者关系后,您可以更新 LiveData
对象的值,这样当用户点按某个按钮时会触发所有观察者
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "Sur";
mModel.getCurrentName().setValue(anotherName);
}});
LiveData总结
1.您可以使用observeForever(observer) 方法注册没有关联 LifecycleOwner对象的观察者。在这种情况下,认为观察者始终处于活跃状态,因此它始终会收到有关修改的通知。您可以通过调用 removeObserver(Observer)方法移除这些观察者。
2.确保将用于更新界面的LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:
避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使 LiveData 对象在配置更改后继续存在。
3.您必须调用 setValue(T) 方法以从主线程更新 LiveData 对象。如果代码在 worker 线程中执行,则您可以改用 postValue(T)方法来更新 LiveData 对象。