2017年Google I/O大会不仅发布了O预览版,同时还将kotlin作为安卓开发的新宠儿。今天要介绍的是Google推出的应用架构组件。
众所周知,安卓开发架构一直很自由,从MVC到MVP、MVVM等,今年I/O大会上Google也推出了一份关于应用架构的最佳实践指南。
Android框架具有明确的API来处理与操作系统的联系点,例如Activity,但这些是进入应用程序的入口点,而不是为应用程序架构构建块,框架组件不会强制您将数据模型与UI组件分开,或提供一种清晰的方式来保持与生命周期无关的数据。
Google官方架构
- 不要在应用程序组件中保存任何应用数据或状态,并且组件间也不应该相互依赖
- 通过 model 驱动应用 UI,并尽可能的持久化
Android Framework中定义的大多数应用程序组件都附有生命周期,这些生命周期由运行的操作系统或框架代码进行管理。它们是Android如何工作的核心,应用程序必须严格遵循它们。不这样做可能会触发内存泄漏甚至应用程序崩溃。
举个栗子:
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// connect to system location service
}
void stop() {
// disconnect from system location service
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// update UI
});
}
public void onStart() {
super.onStart();
myLocationListener.start();
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
上面的代码看起来就像我们平常的代码风格,但在一个真实的应用程序中,可能会遇到大量相似的调用,从而使onStart()和onStop()方法变得非常臃肿。另外如果一些组件不能在onStart()中启动,我们需要在启动位置观察器之前检查一些配置怎么办?在某些情况下,可以在Activity停止后检查完成,这意味着myLocationListener.start()将被调用myLocationListener.stop()之后,基本上保持连接。
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// update UI
});
}
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
Lifecycles 提供了一系列方法,可帮助您以弹性和孤立的方式解决这些问题。
Lifecycles
LIfecycle是一个保存有关组件生命周期状态的信息,并允许其他对象观察此状态的类,将系统组件(Activity、Fragment 等等)的生命周期分离到 Lifecycle 类,Lifecycle 类允许其他类作为观察者,在外部观察系统组件生命周期的变化。它使用两个主要枚举来跟踪其关联组件的生命周期状态。
- Event:这些是从框架和Lifecycle类调度的生命周期事件。这些事件映射到Activity和Fragment中的回调事件。
- State:由Lifecycle对象跟踪的组件的当前状态
首先通过向其方法添加注解@OnLifecycleEvent来监视组件的生命周期状态
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
}
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());
然后在 LifecycleRegistryOwner 比如 LifecycleActivity 加入上面最后一行代码
在Activity和Fragment中实现LifecycleOwner。通过实现内置的LifecycleRegistryOwner接口(而不是扩展LifecycleFragment或LifecycleActivity),任何Activity和Fragment都可以转换为LifecycleOwner
public class MyFragment extends Fragment implements LifecycleRegistryOwner {
LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
如果您有一个要创建LifecycleOwner的自定义类,则可以使用LifecycleRegistry类,但是您需要将事件转发到该类中。如果片段和活动实现了LifecycleRegistryOwner接口,则此转发将自动完成。
如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。
有关LiveData会在后面介绍。
LifecycleOwner
LifeCycleOwner是一个只有一个方法的接口用于表明其有一个LifeCycle对象。这个方法为getLifecycle(),它必须由类实现。
LifecycleOwner从各个类(如Activity和Fragment)抽象生命周期的所有权,并允许编写可与两者兼容的组件。Activity和Fragment只要实现这个接口就能配合LifeCycle实现生命周期监听。任何自定义应用程序类都可以实现LifecycleOwner接口。
注意 : 由于Lifecycles项目处于alpha阶段,因此Fragment和AppCompatActivity类无法实现(因为我们无法将稳定组件的依赖关系添加到不稳定的API)。在Lifecycles稳定之前,为了方便起见,提供了LifecycleActivity和LifecycleFragment类。Lifecycles项目发布后,支持库片段和活动将实现LifecycleOwner接口;当时将不推荐使用LifecycleActivity和LifecycleFragment。
对于上面的示例,我们可以使MyLocationListener类成为LifecycleObserver,并使用onCreate上的生命周期进行初始化,并在此之后不再担心。这允许MyLocationListener类自足,这意味着它可以在必要时进行自己的清理。
class MyActivity extends LifecycleActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// update UI
});
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}
为了简化此用例,Lifecycle类允许其他对象查询当前状态
class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// connect
}
}
public void enable() {
enabled = true;
if (lifecycle.getState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
通过这个实现,我们的LocationListener类是完全生命周期感知的;它可以进行自己的初始化和清理而不受Activity或者Fragment的管理。如果我们需要从另一个Activity或另一个Fragment使用我们的LocationListener,我们需要做的就是初始化它。所有的安装和拆卸操作都将由类本身管理。
可以与Lifecycle一起使用的类称为生命周期感知组件。鼓励提供需要使用Android生命周期的类的库可以提供生命周期感知组件,以便客户端可以轻松地在客户端集成这些类,而无需手动生命周期管理。
LiveData是生命周期感知组件的示例。与ViewModel一起使用LiveData可以在遵循Android生命周期的情况下,更容易地使用数据填充UI
Lifecycle实践准则
- 保持UI控制器(Activity和Fragment)尽可能体积小。他们不应该试图获取他们的数据;而是使用ViewModel来执行此操作,并观察LiveData以将更改反映到视图中
- 尝试编写数据驱动的UI,您的UI控制器的责任是在数据更改时更新视图,或将用户操作通知给ViewModel
- 将您的数据逻辑放在ViewModel类中。 ViewModel应该作为UI控制器和其他应用程序之间的连接器。请注意,ViewModel不是提取数据(例如,从网络)的责任。相反,ViewModel应该调用相应的组件来执行此工作,然后将结果提供给UI Controller。
- 使用数据绑定在您的视图和UI控制器之间具有干净的界面。这将允许您使您的视图更具声明性,并最大限度地减少您需要在活动和片段中写入的更新代码。如果您更喜欢在Java中执行此操作,请使用像Butter Knife这样的库来避免使用样板代码并进行更好的抽象。
- 如果您有一个复杂的UI,请考虑创建一个Presenter类来处理UI修改。这通常是过度的,但可能有助于使您的UI更容易测试。
- 不要在ViewModel中引用View或Activity上下文。如果ViewModel超过活动(在配置更改的情况下),您的活动将被泄露,并且不正确地垃圾回收