Java客户端代码架构的设计
客户端类需要与服务器端进行通信,也需要操作sqlite数据库,我们应该定义不同的类型的类完完成这些职责,降低耦合度。本文将介绍以下内容:“Activity类(界面类)”、“fragment类(界面类)”、“HttpBO类(向服务器发送请求的类)”、“HttpEvent类(封装向服务器发送请求的事件类型)”、“SQLiteBO类(向客户端数据库发送请求的类)”、“SQLiteEvent类(封装向客户端数据库发送请求的事件类型)”、“Presenter类(操作客户端数据库的类)”、“BaseEvent(httpEven和sqliteEvent的父类)”。
1、Activity类(界面类)
Activity类继承了android.support.v7.app.AppCompatActivity类:
public abstract class BaseActivity extends AppCompatActivity {
与前端xml文件绑定,显示组件:
@BindView(R.id.forget_password)
TextView forgetPassword;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login1);
……
监听组件事件:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.login: //点击登录按钮
Date date = new Date(System.currentTimeMillis() + NtpHttpBO.TimeDifference);
初始化Presenter、HttpBO、HttpEvent、SQLiteBO、SQLiteEvent对象:
private static CompanySQLiteEvent companySQLiteEvent = null;
private static CompanyHttpEvent companyHttpEvent = null;
private static CompanySQLiteBO companySQLiteBO = null;
private static CompanyHttpBO companyHttpBO = null;
private StaffPresenter staffPresenter;
……
if (companySQLiteEvent == null) {
companySQLiteEvent = new CompanySQLiteEvent();
companySQLiteEvent.setId(BaseEvent.EVENT_ID_LoginActivity);
}
if (companyHttpEvent == null) {
companyHttpEvent = new CompanyHttpEvent();
companyHttpEvent.setId(BaseEvent.EVENT_ID_LoginActivity);
}
if (companySQLiteBO == null) {
companySQLiteBO = new CompanySQLiteBO(GlobalController.getInstance().getContext(), companySQLiteEvent, companyHttpEvent);
}
if (companyHttpBO == null) {
companyHttpBO = new CompanyHttpBO(GlobalController.getInstance().getContext(), companySQLiteEvent, companyHttpEvent);
}
companySQLiteEvent.setSqliteBO(companySQLiteBO);
companySQLiteEvent.setHttpBO(companyHttpBO);
companyHttpEvent.setSqliteBO(companySQLiteBO);
companyHttpEvent.setHttpBO(companyHttpBO);
……
监听EventBus发送的消息,关于httpEvent和sqliteEvent事件的:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPosHttpEvent(PosHttpEvent event) {
if (event.getId() == BaseEvent.EVENT_ID_LoginActivity) {
……
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPosSQLiteEvent(PosSQLiteEvent event) {
if (event.getId() == BaseEvent.EVENT_ID_LoginActivity) {
……
发送Handler消息:
Message message1 = new Message();
message1.what = 1;
handler.sendMessage(message1);
private Handler handler = new Handler() {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
closeLoadingDialog(loadingDailog);
if (inputCompanySNDialog != null) {
inputCompanySNDialog.dismiss();
}
……
2、fragment类(界面类)
fragment类继承了android.support.v4.app.Fragment:
public class BaseFragment1 extends Fragment
可以使用fragment来创建和组合界面,来满足UI界面的需求。
它的主要组成和activity类似,可以查看activity类介绍。
3、HttpBO类(向服务器发送请求的类)
BaseHttpBO是抽象类,是其它HttpBO类的父类,主要存放一些HttpBO的公共方法和常量:
public abstract class BaseHttpBO {
private Logger log = Logger.getLogger(this.getClass());
protected final long TIME_OUT = GlobalController.HTTP_REQ_Timeout * 1000;
public static final String ERROR_MSG_Network = "网络错误,请检查网络连接!";
public BaseHttpEvent getHttpEvent() {
return httpEvent;
}
public void setHttpEvent(BaseHttpEvent httpEvent) {
this.httpEvent = httpEvent;
}
protected BaseHttpEvent httpEvent;
发送请求前,参数的格式检查和session的有效期检查:
public boolean createNSync(int iUseCaseID, List<BaseModel> list) {
if (list != null){
for(BaseModel bm : list){
if (!checkCreate(iUseCaseID, bm)){
return false;
}
}
}
// 判断SessionID是否为null,如果为null,返回false
if (GlobalController.getInstance().getSessionID() == null) {
httpEvent.setLastErrorCode(ErrorInfo.EnumErrorCode.EC_InvalidSession);
return false;
}
return doCreateNSync(iUseCaseID, list);
}
具体的ModelHttpBO负责向服务器发送Http请求:
@Override
protected boolean doRetrieveNAsync(int iUseCaseID, BaseModel bm) {
log.info("正在执行CommodityHttpBO的retrieveNAsync,bm=" + (bm == null ? null : bm.toString()));
httpEvent.setEventProcessed(false);
Request req = new Request.Builder()
.url(Configuration.HTTP_IP + Commodity.HTTP_Commodity_RETRIEVEN)
.addHeader(BaseHttpBO.COOKIE, GlobalController.getInstance().getSessionID())
.build();
HttpRequestUnit hru = new RetrieveNCommodity();
hru.setRequest(req);
hru.setTimeout(TIME_OUT);
hru.setbPostEventToUI(true);
httpEvent.setStatus(BaseEvent.EnumEventStatus.EES_Http_ToDo);
hru.setEvent(httpEvent);
HttpRequestManager.getCache(HttpRequestManager.EnumDomainType.EDT_Communication).pushHttpRequest(hru);
log.info("正在请求需要同步的Commodity");
return true;
}
4、HttpEvent类(封装向服务器发送请求的事件类型)
BaseHttpEvent是其它HttpEvent的父类:
public class BaseHttpEvent extends BaseEvent
封装服务器返回的数据:
public String getResponseData() {
return responseData;
}
public void setResponseData(String responseData) {
this.responseData = responseData;
}
protected String responseData;
设置向服务器请求的类型:
public void setRequestType(HttpRequestUnit.EnumRequestType requestType) {
this.requestType = requestType;
}
public HttpRequestUnit.EnumRequestType getRequestType() {
return requestType;
}
protected HttpRequestUnit.EnumRequestType requestType;
/**
* RetrieveNC,后面带C的请求的是普通Action。不带C请求的是SyncAction
*/
public enum EnumRequestType {
ERT_RetailTrade_Create("ERT_RetailTrade_Create", 0), //
ERT_Barcodes_RetrieveN("ERT_Barcodes_RetrieveN", 1),
……
设置错误码和错误信息:
public ErrorInfo.EnumErrorCode getLastErrorCode() {
return lastErrorCode;
}
public void setLastErrorCode(ErrorInfo.EnumErrorCode lastErrorCode) {
this.lastErrorCode = lastErrorCode;
}
public String getLastErrorMessage() {
return lastErrorMessage;
}
public void setLastErrorMessage(String lastErrorMessage) {
this.lastErrorMessage = lastErrorMessage;
}
解析服务器返回的数据:
public JSONObject parseError(String responseData) {
try {
log.info("服务器返回的数据是:" + responseData);
JSONObject jsonObject = new JSONObject(responseData);
String sErrCode = jsonObject.getString(BaseModel.JSON_ERROR_KEY);
setLastErrorCode(ErrorInfo.EnumErrorCode.valueOf(sErrCode));
if (getLastErrorCode() != ErrorInfo.EnumErrorCode.EC_NoError) {
String sErrorMsg = (String) jsonObject.get(BaseModel.KEY_HTMLTable_Parameter_msg);
setLastErrorMessage(sErrorMsg);
}
if (getLastErrorCode() == ErrorInfo.EnumErrorCode.EC_DuplicatedSession) {
HttpRequestStatus = 1;
HttpRequestWarnMsg = WARN_Msg_DuplicatedSession;
}
return jsonObject;
} catch (Exception e) {
e.printStackTrace();
// 会话过期时,服务器返回的是一个URL,POS端解析会失败。但可从其头部的sessionStatus知道是不是会话过期
String header = getResponse().header("sessionStatus");
if (header != null && header.equals("timeOut")) {
lastErrorCode = ErrorInfo.EnumErrorCode.EC_SessionTimeout;
HttpRequestStatus = 1;
HttpRequestWarnMsg = WARN_Msg_SessionTimeOut;
} else {
setLastErrorCode(ErrorInfo.EnumErrorCode.EC_OtherError);
}
return null;
}
}
具体model的HttpEvent的关键方法是event,它负责根据不同的请求类型做不同的处理:
@Override
public void onEvent() {
StringUtils.printCurrentFunction("getRequestType", getRequestType());
do {
if (lastErrorCode != ErrorInfo.EnumErrorCode.EC_NoError) {
log.info("网络请求错误," + lastErrorCode);
break;
}
//
JSONObject jsonObject = parseError(getResponseData());
if (jsonObject == null || getLastErrorCode() == ErrorInfo.EnumErrorCode.EC_DuplicatedSession || getLastErrorCode() != ErrorInfo.EnumErrorCode.EC_NoError) {
break;
}
//
switch (getRequestType()) {
case ERT_Commodity_Create:
handleCreate(jsonObject);
break;
case ERT_Commodity_RetrieveN:
handleRetrieveN(jsonObject);
break;
……
5、SQLiteBO类(向客户端数据库发送请求的类)
BaseSQLiteBO是其它SQLiteBO的父类,存放公共的常量和方法:
public abstract class BaseSQLiteBO {
private Logger log = Logger.getLogger(this.getClass());
public static int INVALID_INT_ID = -1;
public static long INVALID_ID = -1l;
public static int INVALID_CASE_ID = -1;
public static int INVALID_STATUS = -1;
public static final int INVALID_Type = -1;
public static final int INVALID_NO = -1;
……
/**
* 向SQLite发送异步DB操作请求:创建对象
*/
public abstract boolean createAsync(int iUseCaseID, final BaseModel bm);
/**
* 向SQLite发送同步DB操作请求:创建对象
*/
public BaseModel createSync(int iUseCaseID, final BaseModel bm) {
throw new RuntimeException("Not yet implemented!");
}
具体Model类的SQLiteBO调用Presenter类的接口,操作数据库:
@Override
public boolean createAsync(int iUseCaseID, BaseModel bm) {
log.info("正在执行CommoditySQLiteBO的createAsync,bm=" + (bm == null ? null : bm.toString()));
switch (sqLiteEvent.getEventTypeSQLite()) {
case ESET_Commodity_CreateAsync:
// sqLiteEvent.setEventTypeSQLite(BaseSQLiteEvent.EnumSQLiteEventType.ESET_Commodity_CreateAsync);
if (GlobalController.getInstance().getCommodityPresenter().createObjectAsync(iUseCaseID, bm, sqLiteEvent)) {
return true;
} else {
log.info("创建commodity失败!");
}
break;
default:
log.info("未定义的事件!");
throw new RuntimeException("未定义的事件!");
}
return false;
}
6、SQLiteEvent类(封装向客户端数据库发送请求的事件类型)
BaseSQLiteEvent是其它SQLiteEvent的父类,存放一些公共常量和方法。
定义枚举sqlite的事件类型:
public class BaseSQLiteEvent extends BaseEvent {
private static int INDEX = 0;
public enum EnumSQLiteEventType {
ESET_Commodity_CreateAsync("ESET_Commodity_CreateAsync", INDEX++), //
ESET_Commodity_CreateNAsync("ESET_Commodity_CreateNAsync", INDEX++), //
ESET_Commodity_UpdateAsync("ESET_Commodity_UpdateAsync", INDEX++), //
ESET_Commodity_Retrieve1Async("ESET_Commodity_Retrieve1Async", INDEX++), //
ESET_Commodity_RetrieveNAsync("ESET_Commodity_RetrieveNAsync", INDEX++),
……
public EnumSQLiteEventType getEventTypeSQLite() {
return eventTypeSQLite;
}
public void setEventTypeSQLite(EnumSQLiteEventType eventTypeSQLite) {
this.eventTypeSQLite = eventTypeSQLite;
}
protected EnumSQLiteEventType eventTypeSQLite;
具体Model的SQLiteEvent,主要方法也是event,根据不同sqlite事件类型做不同处理:
public class CommoditySQLiteEvent extends BaseSQLiteEvent {
private Logger log = Logger.getLogger(this.getClass());
public CommoditySQLiteEvent() {
}
@Override
public void onEvent() {
StringUtils.printCurrentFunction("eventTypeSQLite", eventTypeSQLite);
switch (eventTypeSQLite) {
case ESET_Commodity_CreateNAsync:
if (lastErrorCode == ErrorInfo.EnumErrorCode.EC_NoError) {
log.info("成功插入commodity的List!");
} else {
log.info("插入commodity的List失败!");
lastErrorCode = ErrorInfo.EnumErrorCode.EC_OtherError;
}
status = EnumEventStatus.EES_SQLite_Done;
setEventProcessed(true);
break;
case ESET_Commodity_CreateAsync:
if (lastErrorCode == ErrorInfo.EnumErrorCode.EC_NoError) {
log.info("成功插入commodity");
} else {
log.info("插入commodity失败!");
lastErrorCode = ErrorInfo.EnumErrorCode.EC_OtherError;
}
status = EnumEventStatus.EES_SQLite_Done;
setEventProcessed(true);
break;
7、Presenter类(操作客户端数据库的类)
BasePresenter类是其它Presenter类的父类,存放一些公共方法和常量:
/**
* 本类提供同步接口和异步接口。<br />
* 提供异步接口是为了保证Android界面的流畅性。<br />
* 同步接口大部分在测试代码中使用。异步接口大部分在非测试代码中使用。<br />
*/
public class BasePresenter {
private Logger log = Logger.getLogger(this.getClass());
public static final String SYNC_Type_C = "C"; //创建型同步块
public static final String SYNC_Type_U = "U"; //更新型同步块
public static final String SYNC_Type_D = "D"; //删除型同步块
……
定义错误码和错误信息:
public ErrorInfo.EnumErrorCode getLastErrorCode() {
return lastErrorCode;
}
protected ErrorInfo.EnumErrorCode lastErrorCode;
public String getLastErrorMessage() {
return lastErrorMessage;
}
protected String lastErrorMessage;
定义由子类实现的方法:
protected List<?> createNSync(int iUseCaseID, final List<?> list) {
throw new RuntimeException("Not yet implemented!");
}
参数格式的检查:
public BaseModel createObjectSync(int iUseCaseID, final BaseModel bm) {
String err = bm.checkCreate(iUseCaseID);
if (err.length() > 0) {
Constants.checkModelLog(log, bm, err);
lastErrorCode = ErrorInfo.EnumErrorCode.EC_WrongFormatForInputField;
lastErrorMessage = err;
return null;
}
return createSync(iUseCaseID, bm);
}
具体Model类的Presenter负责操作对应的数据库表:
@PerActivity
public class CommodityPresenter extends BasePresenter {
private Logger log = Logger.getLogger(this.getClass());
@Inject
public CommodityPresenter(final DaoSession dao) {
super(dao);
}
@Override
protected String getTableName() {
return dao.getCommodityDao().getTablename();
}
@Override
protected List<BaseModel> createNSync(int iUseCaseID, final List<?> list) {
log.info("正在进行CommodityPresenter的createNSync,list=" + (list == null ? null : list.toString()));
switch (iUseCaseID) {
default:
try {
for (int i = 0; i < list.size(); i++) {
((Commodity) list.get(i)).setSyncDatetime(new Date(System.currentTimeMillis() + NtpHttpBO.TimeDifference));
((Commodity) list.get(i)).setSyncType(BasePresenter.SYNC_Type_C);
long id = dao.getCommodityDao().insert((Commodity) list.get(i));
((Commodity) list.get(i)).setID(id);
}
lastErrorCode = ErrorInfo.EnumErrorCode.EC_NoError;
} catch (Exception e) {
log.info("执行createNSync失败,错误信息=" + e.getMessage());
e.printStackTrace();
lastErrorCode = ErrorInfo.EnumErrorCode.EC_OtherError;
}
return (List<BaseModel>) list;
}
}
8、BaseEvent(httpEven和sqliteEvent的父类)
BaseEvent是BaseHttpEvent和BaseSQLiteEvent的父类,定义一些公共常量和方法:
public class BaseEvent {
private static int INDEX = 0;
/**
* 不同的事件源/界面发出的事件应该作不同的处理。用以下变量区分不同的事件源
* 比如SynThread收到同步后服务器发回的零售单,而MainActivity上用户查旧单服务器也返回了零售单,双方的事件处理都是onRetailTradeSQLiteEvent(),会引起混乱。
* 混乱表现在:
* 1、是拿前者的数据还是后者的数据显示在查单界面上?
* 2、前者可能有串行的事件1和2。1在修改查单界面的列表时,2也可能的修改查单界面的列表,这会引起ConcurrentModificationException。
* 目前所有的Event默认的ID都是0,所以EVENT_ID_SyncThread必定不能从0计起,这里从1计起。
*/
public static final int EVENT_ID_SyncThread = 1;
public static final int EVENT_ID_MainActivity = EVENT_ID_SyncThread + 1;
public static final int EVENT_ID_SyncDataActivity = EVENT_ID_SyncThread + 2;
定义枚举Event的状态:
public enum EnumEventStatus {
/**
* 准备操作SQLite
*/
EES_SQLite_ToDo("EES_SQLite_ToDo", INDEX++),
/**
* 操作SQLite完毕。操作成功与否未知
*/
EES_SQLite_Done("EES_SQLite_Done", INDEX++),
……
定义常用属性:
/**
* 事件处理后,记录的错误码
*/
protected ErrorInfo.EnumErrorCode lastErrorCode;
protected String lastErrorMessage;
/**
* 事件处理完后,用来存储主表信息
*/
protected List<?> listMasterTable;
/**
* 事件处理完后,用来存储从表信息
*/
protected List<?> listSlaveTable;
/**
* 事件处理完后,用来存储一个对象
*/
protected BaseModel baseModel1;
/**
* 职责:UI收到底层或其它地方post的event后,会在@Subcribe函数中处理事件。
* 其中,onEvent处理所有和UI无关的事情,比如设置status/lastErrorCode/eventProcessed/触发httpBO发起网络请求,和UI有关的全部留给@Subcribe函数的其它代码处理
*/
public void onEvent() {
throw new RuntimeException("Not yet implemented!");
}
/**
* 当事件被UI层处理完后,此值为true,否则为false。
*/
protected boolean isEventProcessed;
/**
* 临时主表对象。其内有从表指针。用于异步事件流处理:异步插入主表时,在插入事件中可以再触发事件插入从表信息,这时需要知道要插入的从表的信息。从本变量中就能得知
*/
protected BaseModel tmpMasterTableObj;
/**
* 用于异步事件流处理。异步成功插入主表后,需要再插从表。这时需要知道相应的BO
*/
protected BaseSQLiteBO sqliteBO;
/**
* 用于异步事件流处理。异步成功插入主表从表后,需要再发出http请求。这时需要知道相应的BO
*/
protected BaseHttpBO httpBO;
/**
* 在Event post给UI之前,有的信息需要带给@Subcribe函数,用来提示用户一些注意事项或下一步动作。这个信息可以记在本变量中。
*/
protected String messageBeforeEventPosted;
/**
* 本地同步服务器相应的某个类型数据。有C、U、D三种类型
*/
protected String syncType;
/**
* 实体类的查询或同步时的分页页码,代表开始页或结束页,不代表其它页码
*/
protected String pageIndex;
public String printErrorInfo() {
return "错误码=" + lastErrorCode + "\t错误信息=" + lastErrorMessage;
}
public String printErrorInfo(Object param) {
return "错误码=" + lastErrorCode + "\t错误信息=" + lastErrorMessage + "\t参数:" + param;
}
……

浙公网安备 33010602011771号