1 开发环境
OS : win7 32bit
Axis2 : 1.6.2
Ksoap : 2.0
Android: 4.0.3
2 基于Axis2的服务器端开发
2.1文件下载
首先需要下载 axis2 的相关jar 包,到 axis 的官方网站即可获得开发的依赖包。 下载地址:
http://axis.apache.org/axis2/java/core/download.cgi
现在最高的版本是1.6.2的,下载axis2-1.6.2-bin.zip和axis2-1.6.2-war.zip压缩文件。 然后需要下载官方提供的axis 的 eclipse 插件工具:ServiceArchive Wizard - Eclipse Plug-in 和CodeGenerator Wizard - Eclipse Plug-in。这些工具可以帮助我们打包(aar)及其生产 客户端调用代码。 下载页面:
http://axis.apache.org/axis2/java/core/tools/index.html
2.2 Axis2安装与测试
· 安装插件
将ServiceArchive Wizard - Eclipse Plug-in和Code GeneratorWizard - Eclipse Plug-in插件的压缩文件解压,然后将plugins文件夹中的jar文件放入eclipse下的plugins文件夹下,重启eclipse。如果你的 eclipse 插件安装成功后,打开elipse后New->Other会看到如下效果:
· 部署 axis2
我们需要将下载下来的axis2-1.6.2-war.zip 中的 axis2.war 这个文件放在我们的tomcat安装 目录下的webapps目录下,启动 tomcat 就会把 war 文件转成一个可以跑起来的 axis2 的项目。Tomcat启动后,在浏览器中输入:http://localhost:8080/axis2/ 可以看到如下界面:
2.3 开发与部署WebService
² 建立一个Java工程,命名为XitianServer;
² 新建一个package,命名为com.wisu.service;
² 新建一个主类(BusinessOperateService.java),一个请求代理类(BusinessProxyRequest.java),一个结果返回代理类(BusinessProxyResponse.java),错误信息类(GenericFault.java)和一个错误类型定义接口(FalutType.java)
² 在工程中新建一个lib文件夹,将与axis2相关的jar文件都包含进去
BusinessOperateService.java
public class BusinessOperateService implements FaultType {
publicBusinessProxyResponse businessOperate(BusinessProxyRequest request){
//设置返回的对象
BusinessProxyResponse response = newBusinessProxyResponse();
GenericFault gen = newGenericFault();
gen.setCode(success); //设置结果代码
gen.setErrorMessage("OK"); //设置处理结果详细信息
String result = "<customer>" +
"<person>" +
"<customerNumber>100</customerNumber>" +
"<firstName>真鲜</firstName>" +
"<lastName>一鸣</lastName>" +
"</person>" +
"<street>西斗门路</street>" +
"<city>杭州</city>" +
"<state>中国</state>" +
"<zip>1010</zip>" +
"<phone>0577-110</phone>" +
"</customer>"; //设置返回的XML报文
//封装返回结果
response.setResultXML(result);
response.setGenericFault(gen);
returnresponse;
}
}
BusinessProxyRequest.java
public class BusinessProxyRequest {
private StringbusinessID; //业务ID值
private StringbusinessType; //业务类型
private StringbusinessXML; //XML请求报文
/**省略了set和get方法**/
}
BusinessProxyResponse.java
public class BusinessProxyResponse {
private StringresultXML; //结果XML报文
privateGenericFault genericFault; //错误信息
/**省略了set和get方法**/
}
GenericFault.java
public class GenericFault {
private int code; //错误代码
private StringerrorMessage; //错误消息
/**省略了set和get方法**/
}
FaultType.java
interface FaultType {
final int success =0; //处理成功
final int interiorError = -1; //内部错误
}
2.4 打包生生aar文件来发布WebService服务
(1) 选择New->Other,然后选择客户端代码生成工具Axis2 widzards下的Axis2 code generator,点击Next。
(2) 选择Axis2 Service Archiver然后点击 Next
(3)在Class File Location中填写:E:\workspace\XitianServer\bin,勾选Include.class files only选项,点击next
(4)选择Skip WSDL选项,点击next:
(5)不做任何处理;
(7) 填写服务名称(Service name,自定义)和类的名称(Class name,包名称+Java类的类名),然后单击load按钮,勾选所要的方法;
(8) 填写aar文件输出路径(Output file location)和aar文件名称(OutputFile Name),然后点击Finish按钮,完成服务的打包;
(9) 将aar包放在E:\apache-tomcat-7.0.47\webapps\axis2\WEB-INF\services路径下,重启Tomcat,在http://localhost:8080/axis2/services/listServices下就可以看到新发布的服务:
3 基于Android的客户端开发
² 新建一个Android工程,命名为XitianClient,Activity所在的Package为:com.wisu.xitiancheng.dao;
² 新建一个Activity,命名为MainActivity;
² 新建一个Package,在Package下新建BusinessProxyRequest.java,BusinessProxyResponse.java,GenericFault.java和CastObject.java,在Android运行环境下,同过Soap发送的自定义类参数和接收的类参数都需要通过实现KvmSerializable接口来进行序列化,自定义类中的成员类变量所指向的类也是需要进行序列化,如GenericFault.java;
² 新建一个文件夹add_lib,加入Ksoap的支持包:ksoap2-android-assembly-3.1.1-jar-with-dependencies.jar,并将add_lib文件夹上选择右击,在Build Path中选择将文件夹设置为Use as SourceFolder ;
² 增加Http通讯权限,在工程下的AndroidManifest.xml中加入以下权限:
<uses-permissionandroid:name="android.permission.CAMERA"/>
<uses-permissionandroid:name="android.permission.VIBRATE"/>
<uses-permissionandroid:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permissionandroid:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
<uses-permissionandroid:name="android.permission.INTERNET"/>
<uses-permissionandroid:name="android.permission.RECEIVE_SMS"/>
<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>
<uses-permissionandroid:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>
<uses-permissionandroid:name="android.permission.WRITE_CONTACTS"/>
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>
工程结构图:
MainActivity.java
public class MainActivity extendsActivity {
TextView tv;
//发布服务的命名空间
private static final StringNAME_SPACE ="http://service.wisu.com";
//BusinessProxyRequest的命名空间,定义了BusinessProxyRequest里的成员变量
private static final StringNAME_SPACE2 ="http://service.wisu.com/xsd";
private static final StringWDSL_LINK ="http://192.168.2.133:8080/axis2/services/BusinessOperateService?wsdl"; //服务路径
private static final StringMETHOD_NAME ="businessOperate"; //方法名称
@Override
public voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv =(TextView)findViewById(R.id.xml_content);
//开辟一个请求线程
Thread thr = newThread() {
public void run(){
try {
StringrequestStr = "<customer>" +
"<person>" +
"<customerNumber>100</customerNumber>" +
"<firstName>真鲜</firstName>" +
"<lastName>一鸣</lastName>" +
"</person>" +
"<street>西斗门路</street>" +
"<city>杭州</city>" +
"<state>中国</state>" +
"<zip>1010</zip>" +
"<phone>0577-110</phone>" +
"</customer>"; //请求XML报文
String businessID = "bid001";
String businessType = "Test";
//封装请求
BusinessProxyRequest request = newBusinessProxyRequest();
request.setBusinessID(businessID);
request.setBusinessType(businessType);
request.setBusinessXML(requestStr);
//将自定义类对象序列化
PropertyInfo pi = newPropertyInfo();
pi.setName("request"); //必须和webservice中的参数名称一致
pi.setValue(request);
pi.setType(BusinessProxyRequest.class);
//封装Soap请求对象
SoapObjectsoaprequest = new SoapObject(NAME_SPACE,METHOD_NAME);
soaprequest.addProperty(pi);
SoapSerializationEnvelopeenvelope =new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut =soaprequest; //报文体
envelope.dotNet = false; //是否为dotNet服务
envelope.setOutputSoapObject(soaprequest);
envelope.addMapping(NAME_SPACE2,"BusinessProxyRequest", request.getClass()); //将请求的BusinessProxyRequest与服务器端的BusinessProxyRequest进行相互映射
envelope.avoidExceptionForUnknownProperty =true;
HttpTransportSE ht = newHttpTransportSE(WDSL_LINK); //建立HTTP连接
ht.call("urn:businessOperate", envelope); //调用WebService的服务
SoapObject result = (SoapObject)envelope.getResponse(); //获得服务返回结果
try {
//解析BusinessProxyResponse对象
BusinessProxyResponse response =(BusinessProxyResponse) CastObject.parseToObject(result,BusinessProxyResponse.class); //将SoapObject对象转化为BusinessProxyResponse
tv.setText(response.getResultXML());
SoapObject generic = (SoapObject)response.getGenericFault();
//解析GenericFault对象
GenericFault genericFault =(GenericFault) CastObject.parseToObject(generic, GenericFault.class); //将SoapObject对象转化为GenericFault
Log.d("Code: ",String.valueOf(genericFault.getCode()));
Log.d("ErrorMessage: " , genericFault.getErrorMessage());
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}
} catch(SoapFault e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} catch(XmlPullParserException e) {
e.printStackTrace();
}
}
};
thr.start();
try {
Thread.sleep(500); //一定需要加休眠时间,否则会发生请求错误,不知道基于什么原理
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
BusinessProxyRequest.java
public class BusinessProxyRequest implements KvmSerializable{
private StringbusinessID; //业务ID值
private StringbusinessType; //业务类型
private StringbusinessXML; //XML请求报文
private static final StringNAME_SPACE2 ="http://service.wisu.com/xsd";
@Override
public ObjectgetProperty(int arg0) {
Objectres = null;
switch(arg0){
case 0:
res= this.businessID;
break;
case 1:
res= this.businessType;
break;
case 2:
res= this.businessXML;
break;
default:
break;
}
return res;
}
@Override
public int getPropertyCount() {
return 3;
}
@Override
public voidgetPropertyInfo(int arg0,Hashtable arg1, PropertyInfo arg2) {
switch(arg0){
case 0:
arg2.type =PropertyInfo.STRING_CLASS;
arg2.name = "businessID";
arg2.namespace = NAME_SPACE2;
break;
case 1:
arg2.type =PropertyInfo.STRING_CLASS;
arg2.name = "businessType";
arg2.namespace = NAME_SPACE2;
break;
case 2:
arg2.type =PropertyInfo.STRING_CLASS;
arg2.name = "businessXML";
arg2.namespace = NAME_SPACE2;
break;
default:
break;
}
}
@Override
public voidsetProperty(int arg0, Object arg1) {
if(arg1== null)return;
switch(arg0){
case 0:
this.businessID =arg1.toString();
break;
case 1:
this.businessType =arg1.toString();
break;
case 2:
this.businessXML =arg1.toString();
break;
default:
break;
}
}
public StringgetBusinessID() {
return businessID;
}
public voidsetBusinessID(String businessID) {
this.businessID =businessID;
}
public StringgetBusinessType() {
return businessType;
}
public voidsetBusinessType(String businessType) {
this.businessType =businessType;
}
public StringgetBusinessXML() {
return businessXML;
}
public voidsetBusinessXML(String businessXML) {
this.businessXML =businessXML;
}
}
BusinessProxyResponse.java
public class BusinessProxyResponse implements KvmSerializable {
private StringresultXML; //结果XML报文
private ObjectgenericFault; //错误信息
public voidsetResultXML(String resultXML) {
this.resultXML =resultXML;
}
public StringgetResultXML() {
return resultXML;
}
public voidsetGenericFault(GenericFault genericFault) {
this.genericFault =genericFault;
}
public ObjectgetGenericFault() {
return genericFault;
}
@Override
public ObjectgetProperty(int arg0) {
Objectres = null;
switch(arg0){
case 0:
res= this.resultXML;
break;
case 1:
res= this.genericFault;
break;
default:
break;
}
return res;
}
@Override
public intgetPropertyCount() {
return 2;
}
@Override
public voidgetPropertyInfo(int arg0,Hashtable arg1, PropertyInfo arg2) {
switch(arg0){
case 0:
arg2.type =PropertyInfo.STRING_CLASS;
arg2.name = "resultXML";
break;
case 1:
arg2.type =PropertyInfo.OBJECT_CLASS;
arg2.name = "genericFault";
break;
default:
break;
}
}
@Override
public voidsetProperty(int arg0, Object arg1) {
if(arg1== null)return;
switch(arg0){
case 0:
this.resultXML =arg1.toString();
break;
case 1:
this.genericFault =arg1;
break;
default:
break;
}
}
}
GenericFault.java
public class GenericFault implements KvmSerializable{
private int code; //错误代码
private StringerrorMessage; //错误消息
public voidsetCode(int code) {
this.code =code;
}
public intgetCode() {
return code;
}
public voidsetErrorMessage(String errorMessage) {
this.errorMessage =errorMessage;
}
public StringgetErrorMessage() {
return errorMessage;
}
@Override
public ObjectgetProperty(int arg0) {
// TODO Auto-generated method stub
Objectres = null;
switch(arg0){
case 0:
res= this.code;
break;
case 1:
res= this.errorMessage;
break;
default:
break;
}
return res;
}
@Override
public intgetPropertyCount() {
return 2;
}
@Override
public voidgetPropertyInfo(int arg0,Hashtable arg1, PropertyInfo arg2) {
switch(arg0){
case 0:
arg2.type =PropertyInfo.INTEGER_CLASS;
arg2.name = "code";
break;
case 1:
arg2.type =PropertyInfo.STRING_CLASS;
arg2.name = "errorMessage";
break;
default:
break;
}
}
@Override
public voidsetProperty(int arg0, Object arg1) {
if(arg1== null)return;
switch(arg0){
case 0:
this.code =(Integer)arg1;
break;
case 1:
this.errorMessage =arg1.toString();
break;
default:
break;
}
}
}
CastObjcet.java
public class CastObject {
public staticKvmSerializable parseToObject(SoapObject soapObject, Class objectClass)throwsIllegalAccessException, InstantiationException{
KvmSerializableresult = (KvmSerializable) objectClass.newInstance();
intnumOfAttr = result.getPropertyCount();
for(int i=0;i<numOfAttr; i++){
PropertyInfo info = newPropertyInfo();
result.getPropertyInfo(i, null,info);
//处理property不存在的情况
try{
result.setProperty(i,soapObject.getProperty(info.name));
}catch(Exceptione){
continue;
}
}
returnresult;
}
}
4 XML字符串解析
4.1 目标XML字符串
<customer>
<person>
<customerNumber>100</customerNumber>
<firstName>真鲜</firstName>
<lastName>一鸣</lastName>
</person>
<street>西斗门路</street>
<city>杭州</city>
<state>中国</state>
<zip>1010</zip>
<phone>0577-110</phone>
</customer>
对应的Java类
Person.java
public class Person{
private StringcustomerNumber;
private StringfirstName;
private StringlastName;
/*省略了set和get方法*/
}
Customer.java
public classCustomer {
private Personperson;
private Stringstreet;
private Stringcity;
private Stringstate;
private Stringzip;
private Stringphone;
/*省略了set和get方法*/
}
4.2 Jibx解析
Jibx的优点在于解析的速度快,编码量少,但是使用Jibx需要引入额外的Jar包,这在服务器端开发不成问题,但是在Android端开发时会增加apk的大小。另外使用Jibx需要手动进行编译相关class和XML文件,需要做不少前期工作,而开发过程中只需要写不到10行的代码就行了。
² 在终端进入到工程所在目录,如E:\workspace\XitianServer;
² 运行:java -cp bin;E:/开源项目/jar/jibx/jibx-tools.jar;E:/开源项目/jar/jibx/log4j-1.2.16.jar org.jibx.binding.generator.BindGen-b Customer.xml com.wisu.model.Customer;其中E:/开源项目/jar/jibx/ 为jibx-tools.jar的路径,Cutomer.xml是生成的XML的名称,com.wisu.model.Customer 为Customer.class的路径;
² 打开Customer.xml,去掉<namespace uri="http://iap.com/dal" default="elements"/>,并将<mapping abstract="true"type-name="ns1:customer" class="com.iap.dal.Customer"></binding>里的内容移动到 <mapping class="com.iap.dal.Customer"name="customer"></mapping>之间
² 运行:java -cpbin;E:/开源项目/jar/jibx/jibx-bind.jarorg.jibx.binding.Compile -v Customer.xml ,在bin/com/wisu/model/目录下增加了JiBX_CustomerCustomer_access.class 和 JiBX_CustomerFactory.class两个文件。
/**
*使用Jibx解析XML字符串
*/
public class BusinessOperateService implements FaultType{
publicBusinessProxyResponse businessOperate(BusinessProxyRequest request){
BusinessProxyResponse response = newBusinessProxyResponse(); //设置返回的对象
try {
InputStream reader =String2InputStream(request.getBusinessXML());
IBindingFactory factory = BindingDirectory.getFactory(Customer.class);
IUnmarshallingContext uctx =factory.createUnmarshallingContext();
Customer customerMessage =(Customer)uctx.unmarshalDocument(reader, null);
Person messageBody =customerMessage.getPerson();
System.out.println("CustomerNumber: " + messageBody.getCustomerNumber());
System.out.println("FirstName: " + messageBody.getFirstName());
System.out.println("LastName: " + messageBody.getLastName());
System.out.println("City: " + customerMessage.getCity());
} catch(JiBXException e) {
e.printStackTrace();
} catch(UnsupportedEncodingException e) {
e.printStackTrace();
}
returnresponse;
}
/**
*将String类型转化为InputStream并设置为UTF-8编码
*/
publicInputStream String2InputStream(String str)throwsUnsupportedEncodingException
{
ByteArrayInputStreamstream = new ByteArrayInputStream(str.getBytes("UTF-8"));
returnstream;
}
}
4.3 Sax解析
Sax是目前主流的解析方式,不需要第三方插件的支持,速度较快,但是需要开发人理解XML结构的逻辑性并需要编写大量的代码。
/**
*使用Sax解析XML字符串
*/
public class SAXXmlService extendsDefaultHandler {
private Personperson;
privateCustomer customer;
private StringcurrentTag; //当前所解析的标签
public staticCustomer getPersonInfo(String xmlStr) throwsParserConfigurationException, SAXException, IOException{
SAXParserFactoryfactory = SAXParserFactory.newInstance();
SAXParser saxParser =factory.newSAXParser();
InputStreaminputStr = String2InputStream(xmlStr); //将String转化为InputStream类型
SAXXmlServicehandler = new SAXXmlService();
saxParser.parse(inputStr,handler);
inputStr.close();
returnhandler.customer;
}
/**
*将String转化为InputStreeam类型
*/
public staticInputStream String2InputStream(String str) throwsUnsupportedEncodingException
{
ByteArrayInputStreamstream = new ByteArrayInputStream(str.getBytes("UTF-8"));
returnstream;
}
public voidcharacters(char[] ch,int start, int length)throws SAXException {
super.characters(ch,start, length);
// 判断当前标签是否有效
if (currentTag !=null){
// //读取标签里面的内容
String value = newString(ch, start, length);
// 如果是Id标签,则读取Id标签的内容设置到Person的ID值上
if ("customerNumber".equalsIgnoreCase(currentTag)){
person.setCustomerNumber(value);
} else if ("firstName".equalsIgnoreCase(currentTag)) {//firstName标签
person.setFirstName(value);
} else if ("lastName".equalsIgnoreCase(currentTag)) {// lastName标签
person.setLastName(value);
} else if ("street".equalsIgnoreCase(currentTag)){ // street标签
customer.setStreet(value);
} else if ("city".equalsIgnoreCase(currentTag)) {
customer.setCity(value);
} else if ("state".equalsIgnoreCase(currentTag)) {
customer.setState(value);
} else if ("zip".equalsIgnoreCase(currentTag)) {
customer.setZip(value);
} else if ("phone".equalsIgnoreCase(currentTag)) {
customer.setPhone(value);
}
}
}
/**
* 解析XML时,当读到结束一个元素标签时
*/
public voidendElement(String uri, String localName, String qName) throwsSAXException {
super.endElement(uri,localName, qName);
// 将当前标签名置空
currentTag = null;
// 如果当前结束的标签名是person的话,代表一个person对象已经读取完毕。将其添加到list后置空
if(qName.equals("person")) {
customer.setPerson(person);
}
if(qName.equals("customer")) {
}
}
/**
* 解析XML时,当开始读取XML文档时
*/
public voidstartDocument() throws SAXException {
super.startDocument();
}
/**
* 解析XML时,当开始读到一个元素标签开始时
*/
public voidstartElement(String uri, String localName, String qName, Attributes attributes)throws SAXException {
super.startElement(uri,localName, qName, attributes);
if(qName.equals("person")) {
person = new Person();
}
if(qName.equals("customer")) {
customer = newCustomer();
}
currentTag =qName;
}
}