公司的服务端开发也是醉了,上一个项目使用webservice,这个项目又使用webservice,天呐,怎么都不会HTTP啊,好吧,由于本人记性超级差,上次用过又忘记了,这次来个大总结,因为webservice使用的是soap协议,官网给出对应开源的框架是ksoap2,所以来总结下ksoap2的用法吧
第一步:项目添加依赖
下载ksoap2的jar包,下载地址:下载ksoap2的jar包
下载完成后将jar包添加进你的项目中
第二步:创建一个网络访问的工具类WebServiceUtil
这个类中的call方法就是专门用来访问WebService的,其实这个类的封装可以自己修改的,网上有很多各式各样的,根据自己的需求去修改吧,下面我把它贴出来,你可以自己进行封装
/** * WebService访问方式的工具类 */ public class WebServiceUtils { // 访问的服务器是否由dotNet开发 public static boolean isDotNet = true; // 线程池的大小,这里创建了5个 private static int threadSize = 5; // 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程 private static ExecutorService threadPool = Executors.newFixedThreadPool(threadSize); // 连接响应标示 public static final int SUCCESS_FLAG = 0; public static final int ERROR_FLAG = 1; // 一般自己公司开发都是需要身份验证的,当然你也可以不验证 private static final String ID_HEADERNAME = "verify method";// 身份验证方法名 private static final String ID_NAME_PARAM = "verify key1";// 身份验证 key private static final String ID_NAME_VALUE = "verify value1";// 身份验证 value private static final String ID_PASSWORD_PARAM = "verify key2";// 身份验证 key private static final String ID_PASSWORD_VALUE = "verify value2";// 身份验证 value /** * 调用WebService接口,此方法只访问过用Java写的服务器,我这里给了5个参数,其实服务端地址和命名空间可以在本类中的属性里写成常量,这样不用每次调用的时候都给这两个参数了,这样只有三个参数了 * * @param endPoint WebService服务器地址 * @param nameSpace 命名空间(在服务器那边是写死了的,固定的,在服务器接口那里可以看见) * @param methodName WebService的调用方法名 * @param mapParams WebService的参数集合,可以为null * @param reponseCallBack 服务器响应接口,我这里已经将这个接口移到外面单独出来了,其实是可以写在这个工具类中的,看代码的最底部,这个接口就写在这个工具类中的,这样做也是可以的 */ public static void call(final String endPoint, final String nameSpace, final String methodName, SimpleArrayMap<String, String> mapParams, final ResponseInterface reponseCallBack) { // 1.创建HttpTransportSE对象,传递WebService服务器地址 final HttpTransportSE transport = new HttpTransportSE(endPoint); transport.debug = true; // 身份验证(如果需要的话) // Element[] header = new Element[1]; // // 传入命名空间与验证的方法名 // header[0] = new Element().createElement(nameSpace, ID_HEADERNAME); // // 创建参数 1 // Element userName = new Element().createElement(nameSpace, ID_NAME_PARAM); // userName.addChild(Node.TEXT, ID_NAME_VALUE); // header[0].addChild(Node.ELEMENT, userName); // // 创建参数 2 // Element password = new Element().createElement(nameSpace, ID_PASSWORD_PARAM); // password.addChild(Node.TEXT, ID_PASSWORD_VALUE); // header[0].addChild(Node.ELEMENT, password); // 2.创建SoapObject对象用于传递请求参数 final SoapObject request = new SoapObject(nameSpace, methodName); // 2.1.添加参数,也可以不传 if (mapParams != null) { for (int index = 0; index < mapParams.size(); index++) { String key = mapParams.keyAt(index); // String value = mapParams.get(key); // 多数情况下,传递的参数都为 String 类型,不过少数情况下会有 boolean 类型,所以用 Object 代替 Object value = mapParams.get(key); request.addProperty(key, value); } } // 3.实例化SoapSerializationEnvelope,传入WebService的SOAP协议的版本号 // 这里有 VER10 VER11 VER12 三种版本,根据自己需要填写 final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); // envelope.headerOut = header; // 身份验证(如果需要的话) envelope.dotNet = isDotNet; // 设置是否调用的是.Net开发的WebService envelope.setOutputSoapObject(request);// 传递参数 // envelope.bodyOut = request; //和上一句envelope.setOutputSoapObject(request);等价 // 4.用于子线程与主线程通信的Handler,网络请求成功时会在子线程发送一个消息,然后在主线程上接收 final Handler responseHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 根据消息的arg1值判断调用哪个接口 if (msg.arg1 == SUCCESS_FLAG) { Log.e("WebServiceResult---", msg.obj.toString()); reponseCallBack.onSuccess((SoapObject) msg.obj);//这里接口中用的是SoapObject的,其实也可以是String,那就要写成(String)msg.obj } else { reponseCallBack.onError((Exception) msg.obj); } } }; // 5.提交一个子线程到线程池并在此线种内调用WebService if (threadPool == null || threadPool.isShutdown()) threadPool = Executors.newFixedThreadPool(threadSize); threadPool.submit(new Runnable() { @Override public void run() { SoapObject result = null; // String result = null;//接口中可以是String try { // 解决EOFException System.setProperty("http.keepAlive", "false"); // 连接服务器,有的服务可能不需要传递 NAMESPACE + methodName,第一个参数传递 null transport.call(nameSpace + methodName, envelope); if (envelope.getResponse() != null) { // 获取服务器响应返回的SoapObject result = (SoapObject) envelope.bodyIn; // SoapObject object = (SoapObject) envelope.bodyIn;//接口中也可以是String // result = object.getProperty(0).toString(); } } catch (IOException e) { // 当call方法的第一个参数为null时会有一定的概念抛IO异常 // 因此需要需要捕捉此异常后用命名空间加方法名作为参数重新连接 e.printStackTrace(); try { transport.call(nameSpace + methodName, envelope); if (envelope.getResponse() != null) { // 获取服务器响应返回的SoapObject result = (SoapObject) envelope.bodyIn; // SoapObject object = (SoapObject) envelope.bodyIn;//接口中也可以是String // result = object.getProperty(0).toString(); } } catch (Exception e1) { // e1.printStackTrace(); responseHandler.sendMessage(responseHandler.obtainMessage(0, ERROR_FLAG, 0, e1)); } } catch (XmlPullParserException e) { // e.printStackTrace(); responseHandler.sendMessage(responseHandler.obtainMessage(0, ERROR_FLAG, 0, e)); } finally { // 将获取的消息利用Handler发送到主线程 responseHandler.sendMessage(responseHandler.obtainMessage(0, SUCCESS_FLAG, 0, result)); } } }); } /** * 设置线程池的大小 * * @param threadSize */ public static void setThreadSize(int threadSize) { WebServiceUtils.threadSize = threadSize; threadPool.shutdownNow(); threadPool = Executors.newFixedThreadPool(WebServiceUtils.threadSize); } /** * 服务器响应接口,在响应后需要回调此接口,在这里已经将这个接口给独立出去咯,在interfaces包中,所以给注释掉 */ // public interface Response { // public void onSuccess(SoapObject result);//当前使用的是SoapObject的 public void onSuccess(String result);//也可以用string表示 // // public void onError(Exception e); // } }看到代码的后面,Response接口被我给注释掉了,其实我是将这个接口给独立出来放到一个单独的类中的,如下,只是变了个名字,变成ResponInterface,其实是一样的
import org.ksoap2.serialization.SoapObject; /** * Created by jjg on 2016/10/18. */ public interface ResponseInterface { public void onSuccess(SoapObject result); public void onError(Exception e); }
第三步:有了这个工具类后,那就好办了,但是我还创建了一个类,专门用来存放那些啥命名空间的、网络地址啥的,前面我说过,其实可以将这些放进WebServiceUtil工具类中,但为了方便和减少代码之间的耦合度,我还是单独给出了一个ServiceConstants类来存放这些常量,如下:
/** * 这个类中存放的是一些服务常量,主要是一些服务器的接口链接 * Created by Eli on 2016/10/8. */ public class ServiceConstants { //服务器的接口总地址 public static final String URL_IMAGE= "http://justec.vicp.net:658"; // webservice服务器执行登入的地址 public static final String URL_BASE = "http://justec.vicp.net:658/ThermometerService.asmx?op="; //web服务的命名空间 public static final String NAME_SPACE = "http://tempuri.org/"; //查询用户是否存在 /** * <QueryUserIsExist xmlns="http://tempuri.org/"> <userName>string</userName> </QueryUserIsExist> */ public static final String METHOD_USER_ISEXIST = "QueryUserIsExist"; //soap请求地址 public static final String SOAP_ACTION = "http://tempuri.org/QueryUserIsExist"; //用户登录 /** * <UserLogin xmlns="http://tempuri.org/"> <userName>string</userName> <password>string</password> </UserLogin> */ public static final String METHOD_LOGIN = "UserLogin"; //用户注册 /** * <UsersRegister xmlns="http://tempuri.org/"> <userName>string</userName> <pwd>string</pwd> </UsersRegister> */ public static final String METHOD_REGISTER = "UsersRegister"; //查询家庭用户是否存在 /** * <QueryUserFamilyIsExist xmlns="http://tempuri.org/"> <userName>string</userName> <UserID>int</UserID> </QueryUserFamilyIsExist> */ public static final String METHOD_FAMILYISEXIST = "QueryUserFamilyIsExist"; //新增家庭用户 /** * <InsertUsersFamily xmlns="http://tempuri.org/"> <userName>string</userName> <UserID>int</UserID> </InsertUsersFamily> */ public static final String METHOD_INSTER_FAMILY = "InsertUsersFamily"; //获取所有家庭成员信息 /** * <GetAllFamilyUsers xmlns="http://tempuri.org/"> <userID>int</userID> </GetAllFamilyUsers> */ public static final String METHOD_GET_FAMILY = "GetAllFamilyUsers"; //删除家庭用户 /** * <userFamilyID>int</userFamilyID> */ public static final String METHOD_DELETE_FAMILY = "DeleteUsersFamily"; /** * 更新家庭成员信息 * <UpdateUsersFamily xmlns="http://tempuri.org/"> * <strJson>string</strJson> * </UpdateUsersFamily> */ public static final String METHOD_UPDATA_USER = "UpdateUsersFamily"; /** * 更新家庭成员信息 * <UpdateUsersFamily xmlns="http://tempuri.org/"> * <strJson>string</strJson> * </UpdateUsersFamily> */ public static final String METHOD_UPDATA_MASTER= "UpdateUsers"; }
看到了吗,因为我这里会用到很多服务端的方法名字,用户登入、用户注册等等都是有各自的方法名的,所以我在WebServiceUtil工具类中也将服务地址和命名空间放进Call参数中,因为我每次访问网络的地址是需要重新组织下的,而不是固定某一个不变的地址的,这样即使以后想要扩展其他网络接口,我可以直接在这里添加服务常量,然后组合成网络地址就可以调用Call方法直接使用了,要是你每次访问的服务地址是固定的,完全可以写成常量放进WebServiceUtil工具类中。
第四步:这一步也是为了编程的扩展性而添加的一个管理类,由于网络端访问的方法名很多,所以我写了个管理类来管理,就像MVP思想一样,我只需要在V层调用一个方法,具体的一些逻辑思路都是M层完成一个道理
/** * 这个类是个操作服务器的类,一系列的登入,注册,删除,查询等等方法集中在这里,可以直接来调用 * Created by jjg on 2016/10/18. */ public class Login_manager { private static Login_manager instance;//实例 public static synchronized Login_manager getInstance() {//获取实例,下次用到这个类不用使用new来创建对象,直接Login_manager.getInstance()就可以 if (instance == null) { instance = new Login_manager(); } return instance; } private Login_manager() {//构造方法 } /** * 注册密码MD5加密(就是注册完毕后将密码加密后上传给服务器,以防原始密码在传送过程中被别人盗用,这样保证密码不会泄露) * * @param info * @return */ public String getMD5(String info) {//给的参数是个String,经过加密后返回的也是一个String try { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(info.getBytes("UTF-8")); byte[] encryption = md5.digest(); StringBuffer strBuf = new StringBuffer(); for (int i = 0; i < encryption.length; i++) { if (Integer.toHexString(0xff & encryption[i]).length() == 1) { strBuf.append("0").append(Integer.toHexString(0xff & encryption[i])); } else { strBuf.append(Integer.toHexString(0xff & encryption[i])); } } return strBuf.toString(); } catch (NoSuchAlgorithmException e) { return ""; } catch (UnsupportedEncodingException e) { return ""; } } /** * 请求登入 * @param context * @param userName * @param pwd * @param responseInterface */ public void requestLogin(Context context, final String userName, final String pwd, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userName", userName + ""); mapParams.put("password", getMD5(pwd)); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在登陆..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_LOGIN, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_LOGIN, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 检测用户是否存在(检测的是注册的那个管理员用户) * * @param context * @param userName * @param responseInterface */ public void requestUserIsExist(Context context, final String userName, ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userName", userName + ""); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_USER_ISEXIST, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_USER_ISEXIST, mapParams, responseInterface); } /** * 请求注册 * @param context * @param userName * @param pwd * @param iamge * @param responseInterface */ public void requestRegister(Context context, final String userName, final String pwd,final String iamge, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userName", userName + ""); mapParams.put("password", getMD5(pwd)); mapParams.put("image", iamge); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在注册..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_REGISTER, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_REGISTER, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 请求添加家庭成员 <InsertUsersFamily xmlns="http://tempuri.org/"> <userName>string</userName> <realName>string</realName> <userID>int</userID> <image>string</image> </InsertUsersFamily> * * @param context * @param userName * @param userId * @param responseInterface */ public void requestAddFamily(Context context, final String userName, final String realName,final String sex,final String age,final int userId, final String imgUrl, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userName", userName); mapParams.put("realName",realName); mapParams.put("sex",sex); mapParams.put("birthday",age); mapParams.put("userID", userId + ""); mapParams.put("image", imgUrl); Log.e("image-------", imgUrl.length() + "----" + imgUrl); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在增加用户..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_INSTER_FAMILY, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_INSTER_FAMILY, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 查询家庭用户是否存在 * * @param context * @param userName * @param userId * @param responseInterface */ public void requestFamilyIsExist(Context context, final String userName, final int userId, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userName", userName + ""); mapParams.put("userID", userId + ""); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在查询用户是否存在..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_FAMILYISEXIST, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_FAMILYISEXIST, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 删除家庭成员 * * @param context * @param userId * @param responseInterface */ public void requestDeleteUserFamily(Context context, final int userId, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userFamilyID", userId + ""); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在删除用户..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_DELETE_FAMILY, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_DELETE_FAMILY, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 获取所有家庭成员信息 * * @param context * @param userId * @param responseInterface */ public void requestGetFamily(Context context, final int userId, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("userID", userId + ""); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在获取用户..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_GET_FAMILY, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_GET_FAMILY, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } /** * 更新家庭成员信息 * <UpdateUsersFamily xmlns="http://tempuri.org/"> * <strJson>string</strJson> * </UpdateUsersFamily> * * UpdateUsersFamilyResult */ public void requestUpdateUsers(Context context, final String strJson, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("strJson", strJson + ""); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在修改信息..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_UPDATA_USER, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_UPDATA_USER, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); Log.e("经过联网向服务器发送过去了Json字符串","服务器将返回修改成功或者失败"); } /** * 更新主用户信息 * <UpdateUsersFamily xmlns="http://tempuri.org/"> * <strJson>string</strJson> * </UpdateUsersFamily> * * UpdateUsersFamilyResult */ public void requestUpdateMaster(Context context, final String strJson, final ResponseInterface responseInterface) { // 参数集合 SimpleArrayMap mapParams = new SimpleArrayMap(); mapParams.put("strJson", strJson + ""); final ProgressDialog dialog2 = ProgressDialog.show(context, null, "正在修改信息..."); dialog2.setCancelable(true); dialog2.setCanceledOnTouchOutside(false); WebServiceUtils.call(ServiceConstants.URL_BASE + ServiceConstants.METHOD_UPDATA_MASTER, ServiceConstants.NAME_SPACE, ServiceConstants.METHOD_UPDATA_MASTER, mapParams, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { dialog2.dismiss(); responseInterface.onSuccess(result); } @Override public void onError(Exception e) { dialog2.dismiss(); responseInterface.onError(e); } }); } }
好了,现在直接可以在Activity中使用了
第五步:开始使用
使用的话我就以登入为例吧,可以直接在Activity中这样调用
Login_manager.getInstance().requestLogin(this, username, usercode, new ResponseInterface() { @Override public void onSuccess(SoapObject result) { String strResult=result.getPrimitivePropertyAsString("UserLoginResult"); JSONArray jsonArray = null; int userID = 0;//这个userID是连接成功后返回的管理员ID,需要保存起来,在刷新数据的时候会用到 try { jsonArray = new JSONArray(strResult); userID = jsonArray.getJSONObject(0).getInt("ID"); } catch (JSONException e) { e.printStackTrace(); } if (userID > 0) { Toast.makeText(Login_Activity.this, "登录成功!", Toast.LENGTH_SHORT).show(); saveUserMaster(userID, username); Intent intent = new Intent(Login_Activity.this, UserActivity.class); startActivity(intent); finish(); } else { Toast.makeText(Login_Activity.this, "登录失败,用户名或密码错误!", Toast.LENGTH_SHORT).show(); } } @Override public void onError(Exception e) { } });
好了,要是没看懂,多看几遍,然后找个服务端口实践下,我这里的服务端口好像已经没有用了,服务已经关闭了。