Android请求Webservice的封装(利用运行时注解和反射实现参数设置和数据解析)

介绍

在工作中使用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
接口说明登录请求
参数序号参数名参数类型参数说明
1userAccountString账号
2passwordString密码
返回信息格式例子
success字段为true
{
    "success":"true",
    "message":{
        "uid":"0000000001",
        "userName":"用户姓名",
        "phoneNo":"手机号",
        "gender":"性别:男or女",
        "headUrl":"头像url"
    }
}
success字段为false
{
    "message":"登录失败,账号或密码错误",
    "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;
            }
        }
    };
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值