介绍
在工作中使用webservice请求的时候需要单独写参数的设置以及请求后的数据解析,于是对这块进行了封装,利用了运行时注解和反射机制实现参数封装,数据返回使用json格式数据,利用反射实现解析封装。具体源码请看这里:https://github.com/zuolongsnail/WebserviceTest
不足:
1.利用运行时注解和反射在使用时效率不高;
2.参数的封装和解析可以使用GSON实现;
具体设计
1.请求参数的封装
这里在定义webservice接口参数的时候有两种方法,一是接口需要几个业务字段就设计几个参数;二是接口只设计一个参数,把接口所有业务字段以键值对的方式都封装到一个json串作为唯一的参数传给服务端,服务端拿到这个参数以后再按键值对方式获取业务字段。
由于webservice服务接收到请求的时候,获取参数是没法利用键值对的方式取参数值,只能通过顺序取值,我们这里利用了注解来排序。以下是定义的注解:
/**
* 自定义注解,封装webservice请求参数
*
* @使用方法 index值为参数顺序,name为参数名称,如果不设置则默认变量名为参数名称
* @WSReqParam(index = 1) private String uid;
* @WSReqParam(name = "userId") private String uid;
*
* @description @Retention: 定义注解的保留策略
* @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包括
* @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
* @Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
* @Inherited //说明子类可以继承父类中的该注解
*
* @Target(ElementType.TYPE) //用于描述类、接口(包括注解类型) 或enum声明
* @Target(ElementType.FIELD) //用于描述字段、枚举
* @Target(ElementType.METHOD) //用于描述方法
* @Target(ElementType.PARAMETER) //用于描述参数
* @Target(ElementType.CONSTRUCTOR) //用于描述构造器
* @Target(ElementType.LOCAL_VARIABLE) //用于描述局部变量
* @Target(ElementType.ANNOTATION_TYPE)//注解
* @Target(ElementType.PACKAGE) //用于描述包
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectReqParam {
/**
* 请求参数的顺序,适用于webservice请求
*
* @description 使用1,2,3,4,5,6,7....定义参数顺序,如果不定义,不会作为参数传递
* @return
* @author zuolong
*/
public int index() default 0;
/**
* 请求参数的名称,适用于webservice和http请求
*
* @description 须与接口文档中的参数名称定义相同,不设置则默认变量名为参数的名称
* @return
* @author zuolong
*/
public String name() default "";
}
那么我们如何定义参数,这里打个比方,登录接口需要两个参数,依次是userAccount和password,给这两个参数分别设置顺序为1和2,index值为参数顺序,name为参数名称,如果不设置则默认变量名为参数名称,如果参数类中需要添加不作为参数的变量,这里只要给变量不设置注解即可,参数类定义如下:
/**
* 登录参数类
*/
public class LoginParams extends BaseReqParams {
@InjectReqParam(index = 1)
public String userAccount;
@InjectReqParam(index = 2)
public String password;
}
通过注解和反射设置webservice请求,代码如下:
/**
* 通过注解反射参数顺序和名称
*
* @return
* @throws IllegalAccessException
* @description
* @author zuolong
*/
private Map<String, Map<String, String>> injectParamsMap()
throws IllegalAccessException {
// <参数顺序index, <参数名称name, 参数值value>>
Map<String, Map<String, String>> paramsIndexMap = new HashMap<String, Map<String, String>>();
Class<? extends BaseReqParams> cls = mReqParams.getClass();
Field[] fields = cls.getDeclaredFields();
// 循环遍历请求参数对象中的值
if (fields != null && fields.length > 0) {
for (Field field : fields) {
// 判断变量是否存在指定的注解
if (field.isAnnotationPresent(InjectReqParam.class)) {
// 获得该成员的annotation
InjectReqParam reqParam = field
.getAnnotation(InjectReqParam.class);
// 封装参数1.通过注解获得该参数的顺序
int index = reqParam.index();
if (index <= 0) {
continue;
}
// 封装参数2.通过注解获得该参数的名称
String name = reqParam.name();
// 如果没有设置name则默认变量名为参数名称
if (TextUtils.isEmpty(name)) {
name = field.getName();
}
// 封装参数3.获取参数值
field.setAccessible(true);
Object object = field.get(mReqParams);
String value = null;
if (object != null) {
value = object.toString();
}
// 封装参数4.存储参数
Map<String, String> paramMap = new HashMap<String, String>();
paramMap.put(name, value);
paramsIndexMap.put(String.valueOf(index), paramMap);
}
}
}
return paramsIndexMap;
}
/**
* 设置webservice请求参数
*
* @param soapObject
* @return
*/
public SoapObject setProperty(SoapObject soapObject) {
try {
Map<String, Map<String, String>> paramsIndexMap = injectParamsMap();
// 封装参数5.参数排序后进行遍历
TreeSet<String> indexTreeSet = new TreeSet<String>(
new IndexComparator());
indexTreeSet.addAll(paramsIndexMap.keySet());// 把参数顺序值放在TreeSet中进行排序
Iterator<String> iterator = indexTreeSet.iterator();
while (iterator.hasNext()) {
String indexKey = iterator.next();
// 封装参数6.获取<参数名称, 参数值>键值对,此时map中仅有一对
Map<String, String> paramMap = paramsIndexMap.get(indexKey);
Iterator<String> paramIterator = paramMap.keySet().iterator();
String nameKey = paramIterator.next();
String value = paramMap.get(nameKey);
soapObject.addProperty(nameKey, value);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return soapObject;
}
2.返回数据的解析
这里我们定义了四种解析类型,在发起请求时定义好需要的解析类型,当然选择哪一种解析时更方便得看接口返回什么样的数据,四种解析类型如下:
/**
* 列表解析类型,对应的返回数据必须严格按照以下格式:
* <ul>
* <li>请求成功:
* {
* "success":"true",
* "message":{
* "root":[
* {
* "data1":"数据字段1",
* "data2":"数据字段2"
* },
* {
* "data1":"数据字段1",
* "data2":"数据字段2"
* }
* ]
* }
* }
* <li>请求失败或错误:
* {
* "success":"false",
* "message":"失败或错误信息"
* }
* </ul>
*/
public static final int LIST_PARSE_TYPE = 1;
/**
* 对象解析类型,对应的返回数据必须严格按照以下格式:
* <ul>
* <li>请求成功:
* {
* "success":"true",
* "message":{
* "data1":"数据字段1",
* "data2":"数据字段2"
* }
* }
* <li>请求失败或错误:
* {
* "success":"false",
* "message":"失败或错误信息"
* }
* </ul>
*/
public static final int OBJECT_PARSE_TYPE = 2;
/**
* JSON解析类型,对应的返回数据必须严格按照以下格式:
* <ul>
* <li>请求成功:
* {
* "success":"true",
* "message":{
* 可以包含任何JSON数据格式
* }
* }
* <li>请求失败或错误:
* {
* "success":"false",
* "message":"失败或错误信息"
* }
* </ul>
*/
public static final int JSON_PARSE_TYPE = 3;
/**
* 结果解析类型,对应的返回数据必须严格按照以下格式:
* <ul>
* <li>请求成功:
* {
* "success":"true",
* "message":"请求响应信息"
* }
* <li>请求失败或错误:
* {
* "success":"false",
* "message":"失败或错误信息"
* }
* </ul>
*/
public static final int RESULT_PARSE_TYPE = 4;
针对不同的解析类型处理如下:
/**
* 解析响应数据
*
* @param response
* @throws Exception
*/
public void parseRespData(String response) throws JSONException,
IllegalAccessException, InstantiationException {
Message msg = new Message();
if (!TextUtils.isEmpty(response)) {
JSONObject jsonObject = new JSONObject(response);
boolean result = JSONParseUtil.getRequestResult(jsonObject);
if (result) {
if (mParseType == LIST_PARSE_TYPE) {// 列表解析类型数据解析
mResp.pageSum = JSONParseUtil.getPageTotal(jsonObject);
mResp.itemTotal = JSONParseUtil.getItemTotal(jsonObject);
JSONArray array = JSONParseUtil.getDatas(jsonObject);
mResp.setList(mEntityCls, array);
// 如果返回数据中无字段,则返回“请求成功无数据”结果
if (mResp.list.size() <= 0) {
msg.what = MessageType.REQ_NODATA;
msg.obj = mContext
.getString(R.string.request_nodata_msg);
// 请求无数据
mHandler.sendMessage(msg);
return;
}
} else if (mParseType == OBJECT_PARSE_TYPE) {// 对象解析类型数据解析
// 数据标识
String respId = mResp.respId;
// 数据类型
DataType dataType = mResp.dataType;
JSONObject objectMessage = JSONParseUtil
.getMessage(jsonObject);
// 如果返回数据中无字段,则返回“请求成功无数据”结果
if (objectMessage.length() <= 0) {
msg.what = MessageType.REQ_NODATA;
msg.obj = mContext
.getString(R.string.request_nodata_msg);
// 请求无数据
mHandler.sendMessage(msg);
return;
}
mResp = mResp.getClass().cast(
JSONParseUtil.reflectObject(mResp.getClass(),
objectMessage));
mResp.respId = respId;
mResp.dataType = dataType;
} else if (mParseType == JSON_PARSE_TYPE) {// JSON解析类型数据解析
msg.what = MessageType.REQ_SUCCESS;
msg.obj = jsonObject;
mHandler.sendMessage(msg);
return;
} else if (mParseType == RESULT_PARSE_TYPE) {// 结果解析类型数据解析
mResp.resultInfo = JSONParseUtil.getResultMsg(jsonObject)
.toString();
}
msg.what = MessageType.REQ_SUCCESS;
msg.obj = mResp;
} else {
msg.what = MessageType.REQ_FAILED;
msg.obj = JSONParseUtil.getResultMsg(jsonObject);
}
} else {
msg.what = MessageType.REQ_FAILED;
msg.obj = mContext.getString(R.string.request_server_error_msg);
}
mHandler.sendMessage(msg);
}
最终通过反射赋值到对象:
/**
* 解析后通过反射赋值到对象
*
* @param clazz
* @param jsonObj
* @return
* @throws Exception
*/
public static Object reflectObject(Class clazz, JSONObject jsonObj)
throws JSONException, IllegalAccessException,
InstantiationException {
Object instance = clazz.newInstance();
Field[] fields = clazz.getDeclaredFields();
String attribute = null;
for (Field field : fields) {
attribute = field.getName();
// 判断返回数据中是否含有相对字段并且不为“null”
if (jsonObj.has(attribute) && !jsonObj.isNull(attribute)) {
String value = jsonObj.get(attribute).toString();
if (TextUtils.isEmpty(value)) {
field.set(instance, "");
continue;
}
if (field.getType() == int.class) {
field.setInt(instance, Integer.parseInt(value));
} else if (field.getType() == float.class) {
field.setFloat(instance, Float.parseFloat(value));
} else if (field.getType() == double.class) {
field.setDouble(instance, Double.parseDouble(value));
} else if (field.getType() == long.class) {
field.setLong(instance, Long.parseLong(value));
} else if (field.getType() == boolean.class) {
field.setBoolean(instance, Boolean.parseBoolean(value));
} else if (field.getType() == JSONArray.class) {
field.set(instance, new JSONArray(value));
} else {
field.set(instance, field.getType().cast(value));
}
} else {
if (!attribute.equals("serialVersionUID")) {// 过滤序列号类中的serialVersionUID字段
if (field.getType() == int.class) {
field.setInt(instance, 0);
} else if (field.getType() == float.class) {
field.setFloat(instance, 0);
} else if (field.getType() == double.class) {
field.setDouble(instance, 0);
} else if (field.getType() == long.class) {
field.setLong(instance, 0);
} else if (field.getType() == boolean.class) {
field.setBoolean(instance, false);
} else if (field.getType() == String.class) {
field.set(instance, "");
} else {
field.set(instance, null);
}
}
}
}
return instance;
}
使用说明
在实现请求时,可以继承请求基类再进行封装,有一定的扩展性。
举例,接口定义如下:
接口名 | login | ||
接口说明 | 登录请求 | ||
参数序号 | 参数名 | 参数类型 | 参数说明 |
1 | userAccount | String | 账号 |
2 | password | String | 密码 |
返回信息格式例子 | |||
success字段为true | | ||
success字段为false | |
实现代码如下:
public class MainActivity extends AppCompatActivity {
private Button btn_start_request;
/**
* webservice请求方法名
*/
private String methodName = "login";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start_request = (Button) findViewById(R.id.btn_start_request);
btn_start_request.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 请求登录接口,接口定义参见login.interface文件
LoginParams params = new LoginParams();
params.userAccount = "admin";
params.password = "123456";
sendLoginReqest(params);
}
});
}
/**
* 发起请求
*
* @param params
*/
private void sendLoginReqest(BaseReqParams params) {
// 使用自定义请求
// LoginRequest loginRequest = new LoginRequest(this, methodName, loginHandler, params, new LoginResp(), null, BasicRequest.JSON_PARSE_TYPE);
// loginRequest.sendRequest();
// 使用基类请求
BaseWebserviceRequest loginRequest = new BaseWebserviceRequest(this, methodName, loginHandler, params, new LoginResp(), null, BasicRequest.OBJECT_PARSE_TYPE);
loginRequest.sendRequest();
}
/**
* 数据返回以后在主线程进行业务处理
*/
private Handler loginHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
int what = msg.what;
switch (what) {
case MessageType.REQ_SUCCESS:
if (msg.obj instanceof LoginResp) {
LoginResp resp = (LoginResp) msg.obj;
String uid = resp.uid;
String userName = resp.userName;
String phoneNo = resp.phoneNo;
String gender = resp.gender;
String headUrl = resp.headUrl;
}
break;
default:
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
}
}
};
}