今天,我想与大家分享开发Android应用程序时保持状态的另一种方法。 随时可以终止我们在后台的应用程序并不是什么秘密,而随着积极的节能措施的
问世 ,这个问题变得更加紧迫-您好
Oreo 。 同样,没有人取消手机上的配置更改:方向,语言更改等。 为了从后台打开应用程序并在最后状态下显示界面,我们需要注意保存它。 哦,这个
onSaveInstanceState 。

他给我们带来了多少痛苦。
稍后将使用
Clean Achitecture和
Dagger2给出示例,因此请为此做好准备:)
根据任务维护状态的问题可以通过几种方式解决:
- 将主要数据保存在主机(活动,片段)的onSaveInstanceState中 -例如页面标识符,用户等。 我们需要进行初始数据采集和页面显示。
- 将收到的数据保存在资源库中的交互式程序中(SharedPreference,数据库。
- 重新创建活动时,使用rethein片段保存和恢复数据。
但是,如果我们需要恢复ui的状态以及接口对用户操作的当前反应,该怎么办? 为简单起见,让我们使用一个真实的示例来考虑解决此问题的方法。 我们有一个登录页面-用户输入数据,单击按钮,然后他接到来电。 我们的应用程序进入后台。 系统杀死了他。 听起来很吓人,不是吗?)
用户返回到应用程序,他应该看到什么? 至少,继续登录操作并显示进度。 如果该应用程序在调用主机的onDestroy方法之前成功登录,则用户将看到导航到该应用程序的“开始”屏幕。 使用状态机可以轻松解决此问题。
Yandex的很好的
报告 。 在同一篇文章中,我将尝试分享对这份报告的看法。
现在有一点代码:
基态public interface BaseState<VIEW extends BaseView, OWNER extends BaseOwner> extends Parcelable{ @NonNull String getName(); void onEnter(@NonNull VIEW aView); void onExit(); void forward(); void back(); void invalidateView(@NonNull VIEW aView); @NonNull OWNER getOwner(); void setOwner(@NonNull OWNER aOwner); }
基础管理员 public interface BaseOwner<VIEW extends BaseView, STATE extends BaseState> extends BasePresenter<VIEW>{ void setState(@NonNull STATE aState); }
BasestateImpl public abstract class BaseStateImpl<VIEW extends BaseView, OWNER extends BaseOwner> implements BaseState<VIEW, OWNER>{ private OWNER mOwner; @NonNull @Override public String getName(){ return getClass().getName(); } @Override public void onEnter(@NonNull final VIEW aView){ Timber.d( getName()+" onEnter");
在我们的案例中,状态所有者将是演示者。
查看登录页面,可以区分三种独特的状态:
LoginInitState ,
LoginProgressingState ,
LoginCompleteState 。
因此,现在考虑在这些状态下会发生什么。
LoginInitState我们具有字段验证功能,如果验证成功,登录按钮将变为活动状态。
在
LoginProgressingState中发出一个
Login请求,保存一个令牌,并作出其他请求以启动应用程序的主要活动。
LoginCompleteState导航到应用程序的主屏幕。
有条件的状态下的过渡可以显示在下图中:

如果登录操作在
LoginCompleteState状态下
成功 ,并且
LoginInitState失败 ,则退出
LoginProgressingState状态。 因此,当我们的观点脱离时,我们就可以完全确定演示者的状态。 我们必须使用标准的android机制
onSaveInstanceState保存此状态。 为了使我们能够做到这一点,所有登录状态都必须实现
Parcelable接口。 因此,我们正在扩展基本接口
BaseState 。
接下来,我们有一个问题,如何将该状态从演示者转发到主持人? 最简单的方法是向演示者请求来自主机的数据,但是从体系结构的角度来看,这看起来不太好。 因此保留碎片会为我们提供帮助。 我们可以为缓存创建一个接口,并在此片段中实现它:
public interface Cache{ void saveCacheData(@Nullable Parcelable aData); @Nullable Parcelable getCacheData(); boolean isCacheExist(); }
接下来,我们将缓存片段注入到交互器的构造函数中,例如Cache。 我们在集成器中添加方法以获取状态并将其保存在缓存中。 现在,随着演示者状态的每次更改,我们都可以将状态保存在交互器中,然后交互器又保存在缓存中。 一切都变得非常合乎逻辑。 首次加载主机时,演示者从交互器接收状态,交互器又从缓存接收数据。 这是演示者中状态更改方法的外观:
@Override public void setState(@NonNull final LoginBaseState aState){ mState.onExit(); mState = aState; clearDisposables(); mState.setOwner(this); mState.onEnter(getView()); mInteractor.setState(mState); }
我想指出这一点-通过缓存保存数据可以针对任何数据完成,而不仅限于状态。 您可能需要制作自己的独特代码段缓存来存储当前数据。 本文介绍了一种通用方法。 我还要指出,有关情况非常夸大。 在生活中,您必须解决更为复杂的问题。 例如,在我们的应用程序中,将三个页面组合在一起:登录,注册,密码恢复。 在这种情况下,状态图如下:

结果,使用本文中描述的状态模式和方法,我们能够使代码更具可读性和可维护性。 重要的是还原应用程序的当前状态。
完整的代码可以
在存储库中查看。