3个界面中有3个独立控件,须要同步他们的状态,即其中任一控件状态变化,其他两个随之而变。git
这是最容易想到的方案,实现步骤以下:github
Intent
中startActivityForResult()
跳转到界面BsetResult()
将控件状态返回给界面AonActivityResult()
中获取控件状态并更新UI但该方案有缺点:bash
onActivityResult()
中还夹杂着其余业务逻辑。Activity
间的耦合(即Activiy B
依赖于Activity A
的特殊传值方式,Activity A
依赖于Activity B
的回传值)。由于界面间是两两耦合的,因此也致使了扩展性较差,若是需求改为“从Activity A
直接跳转到Activity B
”,须要从新出处理Activity A
到Activity B
的跳转逻辑。既然经过传递值的方式不够好,那直接“共享值”呢?即将每次状态改变都持久化(存在本地),每次绘制界面都从本地读取状态。服务器
设想界面A中有一个列表,每一个表项都包含一个须要状态同步的控件,当服务器返回一批新数据后,须要挨个将数据进行存储,随着列表不断刷新,本地存储的内容就不断增多,为控制本地存储占用的空间,在 App 退出时需清空本地存储。dom
既然在 App 退出时须要清空数据,则代表控件状态信息的生命周期和 App 的生命周期同步,而持久化解决的问题是生命周期长于 App 生命周期的状况。因而第三个解决方案就闪亮登场了~~~ide
LiveData
是谷歌在Google I/O 2017发布的Android Architecture Components(Google教你如何写 App 系列)中的一项内容。post
对于当前这个case,LiveData
充当以下角色:ui
LiveData
是一个数据持有者,但不像通常的数据持有者,它能够感知系统组件的生命周期。LiveData
能够被观察,但它不像通常的观察者模式(一有数据变更就通知全部观察者)。只有当被观察者处于激活状态时才被通知。因此基于LiveData
的解决方案以下:将控件状态信息保存在LiveData
中,三个不一样的界面分别观察LiveData
。this
经过观察者模式将方案1中数据传递问题转换为数据共享,三个界面没有丝毫耦合。将LiveData设置为单例,使其和 App 生命周期相一致,也避免了开辟额外的本地存储。spa
将要共享的状态信息封装成实体类,简单起见,demo将状态信息设置为int
值,以下:
public class Status {
private int level;
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
}
复制代码
下面的代码只是将状态信息实体类和LiveData
关联,并将LiveData
定义为单例,方便跨界面使用。
public class StatusLiveData extends MutableLiveData<Status> {
private StatusLiveData() {
}
private static class Holder {
public static final StatusLiveData INSTANCE = new StatusLiveData();
}
public static StatusLiveData getInstance() {
return Holder.INSTANCE;
}
}
//MutableLiveData在LiveData基础上暴露两个设值接口
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
复制代码
LiveData
的观察者一般是带有生命周期概念的组件,好比Activity,Fragment等等。观察者需实现Observer<T>
接口,以定义数据变化时作出的响应。
public class ActivityA extends AppCompatActivity implements View.OnClickListener, Observer<Status> {
private int level;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
StatusLiveData.getInstance().observe(this, this);
}
...
@Override
public void onChanged(@Nullable Status status) {
/**
* get status data when it is changed and update UI
*/
int level = status.getLevel();
changeArrowStatus(level);
}
}
复制代码
最后一步就是在状态值变化时候调用LiveData.setValue()
更新数据。这里的逻辑和具体业务相关,demo中的业务场景是点击ImageView
控件时改变其图片。
public class ActivityB extends AppCompatActivity implements View.OnClickListener, Observer<Status> {
private int level;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
findViewById(R.id.iv_arrow).setOnClickListener(this);
...
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
...
case R.id.iv_arrow:
changeArrowStatus(++level);
putStatus(level);
break;
}
}
/**
* put status data into LiveData when data is changed
*/
private void putStatus(int level) {
Status status = new Status();
status.setLevel(level);
StatusLiveData.getInstance().setValue(status);
}
private void changeArrowStatus(int level) {
ImageView ivArrow = findViewById(R.id.iv_arrow);
LevelListDrawable levelListDrawable = ((LevelListDrawable) ivArrow.getDrawable());
levelListDrawable.setLevel(level % 2);
}
}
复制代码
抛砖引玉,若你们有更好的方案,欢迎交流~~