标记字段
代码中有时候有这种需求:需要一个公共访问的标记字段,以下称为标记字段。
下面是案例:
一个订单详情页面,如果页面在显示中,程序中其它地方需要访问这个“正在查看中”的订单信息,订单详情页面打开和关闭时负责设置和清除公共字段所标记的订单对象。在像Android这样的设备上,假设页面可以打开多个,但是用户只会看到最上面的一个,那么此时只有处在任务栈最上面的OrderDetailActivity对象在其onStart和onStop中负责标记的订单的设置。假设有1和2两个详情页面依次被打开,然后依次关闭2、1,其onStart、onStop的执行顺序可能是像下面的:
09-26 11:55:10.808 5493-5493/? D/hxwcc: onStart + @1
09-26 11:55:14.840 5493-5493/? D/hxwcc: onStart + @2
09-26 11:55:15.228 5493-5493/? D/hxwcc: onStop + @1
09-26 11:55:16.672 5493-5493/? D/hxwcc: onStart + @1
09-26 11:55:17.076 5493-5493/? D/hxwcc: onStop + @2
09-26 11:55:19.548 5493-5493/? D/hxwcc: onStop + @1
可见onStart和onStop的执行是有交叉的。在对标记字段进行赋值时,需要考虑这种“相互干扰”,这里的执行都是在UI线程中执行,多线程环境下当然更容易产生这样的交叉赋值。从需求上看,不是当前对象设置的标记它就不应该去清除,因为另一个对象在重新设置标记字段的值得时候自动清除了上一标记值。
可以使用一个额外的字段记录对标记进行赋值的对象,然后通过比较当前对象标记试图操作的对象和之前设置已有标记值的对象就可以得到需要的标记作用。
下面设计一个类型FlagField来组合标记值和标记人,使得标记字段的含义更加内聚。
FlagField类型
代码如下:
/**
* 标记字段,用来存储被公共访问的带有赋值者信息的数据。赋值者在合适的时间赋值,
* 之后可以清除标记值,如果中间有其它
* 赋值者重新标记则清除操作不做任何动作——访问者继续 访问新的标记。
*/
public class FlagField<T> {
private T field;
private Object provider;
public void mark(T field, Object provider) {
synchronized (this) {
this.field = field;
this.provider = provider;
}
}
public T getField() {
synchronized (this) {
return this.field;
}
}
public Object getProvider() {
synchronized (this) {
return this.provider;
}
}
public boolean isMarked() {
synchronized (this) {
return field != null;
}
}
public void clear(Object provider) {
synchronized (this) {
if (provider == this.provider) {
field = null;
provider = null;
}
}
}
}
上面提供了线程安全的版本,如果不需要多线程控制去掉synchronized (this)即可。
使用案例如下:
public class OrderDetailActivity extends Activity {
/** 当前正在被查看的订单id */
public static FlagField<Integer> viewingOrderId = new FlagField<Integer>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewingOrderId.mark(10086, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
viewingOrderId.clear(this);
}
}
(本文是由Atom编写)