书承上文,这次我们来探究一下生命周期绑定和双向绑定,然后会介绍一种架构模式MVVM。
一、双向绑定(tow-way binding)前面我们讲的都是单项绑定策略,即数据单方面通知视图已经更新。这里我们就来讲讲双向绑定的策略。
先来看看现实中一个很简单的需求吧。假如我们现在有一个TextView和一个EditText控件,其中EditText使用的是密码本格式,要求我们在用户输入信息未提交时,显示某种算法加密后的密文时什么样的,显式在TextView中。
按照一般的方法,我们可以先设置一个TextChange的事件,它会在EditText文本变换时读取变换后的文本,然后将它通过加密获得密文,最后使用setText方法写入TextView的。这个方法固然可以,但是却有很多代码,增加了代码的复杂性,降低了可阅读性。
因此,我们可以使用双向绑定数据的办法。双向绑定方法的使用非常简单,与单项基本上是相似的,只是在使用在布局文件定义的变量时吧@{变量}改为@={变量}即可双向绑定。使用@={}方式绑定后,绑定的属性值变化会通知到绑定的变量,使变量可以做出相应的反应。
我们继续使用前面文章例子中的变量book,在布局文件中添加一个EditText控件,具体代码如下。
...... ......
可以看到,我在变量声明的地方引入了base64这个类来充当加密类。EditText使用了双向绑定,TextView使用了单向绑定。运行一下查看结果
可以看到,上面的文字就是下面输入框文字的加密。如果编辑下面的文字,上面的文字是同步更改的。
二、生命周期绑定到这里为止,我们发现,与数据绑定的往往都是视图控件,而Android中视图控件是有生命周期的。随着用户对界面的切换、系统对资源的调配等等,控件会执行不同的生命周期方法,甚至是被销毁。那么就会产生两个问题。
- 数据更新时通知了一个被销毁的对象
- 数据更新通知了一个暂时不需要更新的对象
这些情况都会导致异常或者资源的浪费。因此,Google就推出了生命周期绑定的方案。即LiveData。
生命周期绑定允许控件和它的生命周期持有者同时绑定到数据源,这种情况下数据更新时就会检查一下这些控件是不是处于活动状态(START或者RESUME),是的话就立刻更新,不是的话就暂时不通知。我们来看看代码案例。
首先创建一个数据源类MyDataSource,如下(MutableLiveData是LiveData的增强类)
public class MyDataSource {
public static final MutableLiveData LIVE_DATA = new MutableLiveData<>();
static {
LIVE_DATA.setValue("silencer");
}
}
我们再创建两个活动,LDActivity1和LDActivity2,用来观察处于活动和非活动状态下更新的情况。布局文件如下
LDActivity1中有一个Button和EditView,EditView用来显示绑定的数据,Button用来启动第二个活动。
我们对两个活动的EditText都设置监听方法,以便于在数据变化时更新到EditText可以被我们感知到。同时我们通过在活动的各个生命周期方法中打印来观察它的生命周期变化。代码如下
public class LDActivity1 extends AppCompatActivity implements TextWatcher, View.onClickListener {
private LDBinding1 ldBinding1;
private EditText et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ldBinding1 = DataBindingUtil.setContentView(this,R.layout.activity_ldactivity1);
ldBinding1.setLifecycleOwner(this);
et = ldBinding1.ld1Et;
et.addTextChangedListener(this);
MyDataSource.LIVE_DATA.observe(this, new Observer() {
@Override
public void onChanged(String s) {
et.setText(s);
}
});
ldBinding1.ld1Btn.setonClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
System.out.println("onStart:LDActivity1开始");
}
@Override
protected void onResume() {
super.onResume();
System.out.println("onResume:LDActivity1继续");
}
@Override
protected void onPause() {
super.onPause();
System.out.println("onPause:LDActivity1暂停");
}
@Override
protected void onStop() {
super.onStop();
System.out.println("onStop:LDActivity1停止");
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
System.out.println("数据更新了,我是LDActivity1:"+et.getText());
}
@Override
public void onClick(View v) {
Intent intent = new Intent(LDActivity1.this,LDActivity2.class);
startActivity(intent);
}
}
public class LDActivity2 extends AppCompatActivity implements TextWatcher{
private LDBinding2 ldBinding2;
private EditText et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ldBinding2 = DataBindingUtil.setContentView(this,R.layout.activity_ldactivity2);
ldBinding2.setLifecycleOwner(this);
et = ldBinding2.ld2Et;
et.addTextChangedListener(this);
MyDataSource.LIVE_DATA.observe(this, s -> et.setText(s));
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyDataSource.LIVE_DATA.postValue("查看打印信息"+System.currentTimeMillis());
System.out.println("数据更新完成:"+"查看打印信息"+System.currentTimeMillis());
}
}).start();
}
@Override
protected void onStart() {
super.onStart();
System.out.println("onStart:IDActivity2开始");
}
@Override
protected void onResume() {
super.onResume();
System.out.println("onResume:IDActivity2继续");
}
@Override
protected void onPause() {
super.onPause();
System.out.println("onStop:IDActivity2暂停");
}
@Override
protected void onStop() {
super.onStop();
System.out.println("onStop:IDActivity2停止");
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
System.out.println("数据更新了,我是LDActivity2:"+et.getText());
}
}
看代码我们抓住重点,我们着重看生命周期绑定代码哪里。它调用了一个observe方法,它接收两个参数,第一个是生命周期持有者,第二个是被通知时你要做出的反应。那么问题来了,我们这里只是传入了一个this,即Activity对象,哪来的生命周期持有者。
我们查看AppCompatActivity类的父类,一直追朔上去直到类ComponentActivity类,这个类实现了一个接口LifeCycleOwner,这就是生命周期持有者接口。因此,活动中的控件、碎片等等,都应该绑定到它们所在的活动之中。
运行一下,查看结果。
可以看到,数据变化时仅仅只通知了LDActivity2,因为此时LDActivity1处于非活动状态。当我们点击Back键返回时,就会通知LDActivity1
那么DataBinding的知识我们先讲到这,了解更多可以查看Android官方相关文档。
本BLOG为原创文章,未经本人许可,不得用于商业用途。转载请注明出处并告知本人,否则保留追究法律责任的权利。



