上一篇我们讲了如何封装Android调用WebService的能力,把上一章的类加入我们便有了与WebService通讯的能力。往往我们会遇到WebService调用是通过对象来进行实际交互调用的。于是便有了这一章构建对象传递。

首先我们了解一下。

Ksoap2这个开源包里面提供了一个接口

/*Copyright(c)2003,2004,StefanHaustein,Oberhausen,Rhld.,Germany**Permissionisherebygranted,freeofcharge,toanypersonobtainingacopy*ofthissoftwareandassociateddocumentationfiles(the"Software"),todeal*intheSoftwarewithoutrestriction,includingwithoutlimitationtherights*touse,copy,modify,merge,publish,distribute,sublicense,and/or*sellcopiesoftheSoftware,andtopermitpersonstowhomtheSoftwareis*furnishedtodoso,subjecttothefollowingconditions:**Theabovecopyrightnoticeandthispermissionnoticeshallbeincludedin*allcopiesorsubstantialportionsoftheSoftware.**THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANYKIND,EXPRESSOR*IMPLIED,INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY,*FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT.INNOEVENTSHALLTHE*AUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM,DAMAGESOROTHER*LIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,ARISING*FROM,OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOROTHERDEALINGS*INTHESOFTWARE.**Contributor(s):JohnD.Beatty,F.Hunter,RenaudTognelli***/packageorg.ksoap2.serialization;importjava.util.Hashtable;/***Providesgetandsetmethodsforproperties.Canbeusedtoreplace*reflection(tosomeextend)for"serialization-aware"classes.Currentlyused*inkSOAPandtheRMSbasedkobjectsobjectrepository*/publicinterfaceKvmSerializable{/***Returnsthepropertyataspecifiedindex(forserialization)**@paramindex*thespecifiedindex*@returntheserializedproperty*/ObjectgetProperty(intindex);/***@returnthenumberofserializableproperties*/intgetPropertyCount();/***Setsthepropertywiththegivenindextothegivenvalue.**@paramindex*theindextobeset*@paramvalue*thevalueoftheproperty*/voidsetProperty(intindex,Objectvalue);/***Fillsthegivenpropertyinforecord.**@paramindex*theindextobequeried*@paramproperties*informationaboutthe(de)serializer.Notfrequentlyused.*@paraminfo*Thereturnparameter,tobefilledwithinformationaboutthe*propertywiththegivenindex.*/voidgetPropertyInfo(intindex,Hashtableproperties,PropertyInfoinfo);}

接口的有这么一句话in kSOAP and the RMS based kobjects object repository,大致意思应该就是基于对象存储的时候可以用到他。(当然借助翻译工具翻译的,有什么理解上错误的请联系我)

那么意味着我们只需要把要传递的对象实现这个接口就可以实现对象传输了!

于是乎就有很多网文实现教你如何去实现了!我示例一下!

publicTestimplementsKvmSerializable{publicStringtest1;publicStringtest2;//Returnsthepropertyataspecifiedindex(forserialization)//通过索引返回特定属性(翻译:返回属性在指定的索引(序列化))@OverridepublicObjectgetProperty(intindex){//根据接口注释最直接的会如下操作switch(index){...(returntest1之类)}}//returnthenumberofserializableproperties//返回属性的个数(翻译:返回的数量可序列化的属性)@OverridepublicintgetPropertyCount(){//TODOAuto-generatedmethodstub//返回固定数量return2;}//Setsthepropertywiththegivenindextothegivenvalue.//根据index给PropertyInfo赋值参数(翻译:属性与给定的索引设置为给定值。)@OverridepublicvoidgetPropertyInfo(intindex,Hashtablearg1,PropertyInfoa){//根据接口注释最直接的会如下操作swtich(index){...(设置a的属性值)}}//Fillsthegivenpropertyinforecord.//给相应索引的属性赋值(翻译:填充给定属性信息记录。)@OverridepublicvoidsetProperty(intindex,Objectarg1){switch(index){...(test1=arg1之类)}}}

这样是没有错误的,但是在我们有很多不同的类需要传递的时候呢?这个类属性上百个的时候呢?

那我们岂不是一直需要做重复操作。那么我们何不写一个通用的转换类!

于是在不考虑更复杂,以及特定的一些数据类型的时候我们有了下面这个类:

importjava.lang.reflect.Field;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;importjava.lang.reflect.ParameterizedType;importjava.lang.reflect.Type;importjava.util.ArrayList;importjava.util.Hashtable;importjava.util.List;importjava.util.Vector;importorg.ksoap2.serialization.KvmSerializable;importorg.ksoap2.serialization.PropertyInfo;importorg.ksoap2.serialization.SoapObject;/***对象传输基础类*@author刘亚林*@e-mail461973266@qq.com**/publicabstractBaseKvmSerializableimplementsKvmSerializable{/****将首字母大写**/publicstaticStringfristUpperCase(Stringstr){returnString.valueOf(str.charAt(0)).toUpperCase().concat(str.substring(1));}//Returnsthepropertyataspecifiedindex(forserialization)//通过索引返回特定属性(翻译:返回属性在指定的索引(序列化))@OverridepublicObjectgetProperty(intindex){//既然是要返回特定索引的属性值,那么我们何不直接通过反射取对应属性返回Field[]fs=this.getClass().getDeclaredFields();Fieldf=fs[index];Stringname=f.getName();name=fristUpperCase(name);StringgetMethodName="get";if(f.getType()==boolean.class||f.getType()==Boolean.class){getMethodName="is";}getMethodName+=name;MethodgetMethod;Objectval=null;try{getMethod=this.getClass().getMethod(getMethodName);getMethod.setAccessible(true);val=getMethod.invoke(this);}catch(NoSuchMethodExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(IllegalAccessExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(IllegalArgumentExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(InvocationTargetExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}returnval;}//returnthenumberofserializableproperties//返回属性的个数(翻译:返回的数量可序列化的属性)@OverridepublicintgetPropertyCount(){//TODOAuto-generatedmethodstub//返回固定数量returnthis.getClass().getDeclaredFields().length;}//Setsthepropertywiththegivenindextothegivenvalue.//根据index给PropertyInfo赋值参数(翻译:属性与给定的索引设置为给定值。)@OverridepublicvoidgetPropertyInfo(intindex,Hashtablearg1,PropertyInfoa){Field[]fs=this.getClass().getDeclaredFields();Fieldf=fs[index];Stringname=f.getName();//主要是设置type和name其他的需要可以继续添加a.type=getTypeByClass(f.getType());a.name=name;}//Fillsthegivenpropertyinforecord.//给相应索引的属性赋值(翻译:填充给定属性信息记录。)@OverridepublicvoidsetProperty(intindex,Objectarg1){Field[]fs=this.getClass().getDeclaredFields();Fieldf=fs[index];Stringname=f.getName();name=fristUpperCase(name);StringsetMethodName="set"+name;Methodm;try{m=this.getClass().getDeclaredMethod(setMethodName,f.getType());m.setAccessible(true);m.invoke(this,arg1);}catch(NoSuchMethodExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(IllegalAccessExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(IllegalArgumentExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}catch(InvocationTargetExceptione){//TODOAuto-generatedcatchblocke.printStackTrace();}}/****根据类别获得PropertyInfo特定类别**实际上除了统一类别这个没什么太多用为了心里好过而加**你看下面对于这些类别的的定义就知道了**publicstaticfinalClassOBJECT_CLASS=newObject().getClass();**publicstaticfinalClassSTRING_CLASS="".getClass();**publicstaticfinalClassINTEGER_CLASS=newInteger(0).getClass();**publicstaticfinalClassLONG_CLASS=newLong(0).getClass();**publicstaticfinalClassBOOLEAN_CLASS=newBoolean(true).getClass();**publicstaticfinalClassVECTOR_CLASS=newjava.util.Vector().getClass();**/publicClassgetTypeByClass(Classcls){if(cls.isAssignableFrom(Boolean.class)||cls.isAssignableFrom(boolean.class)){returnPropertyInfo.BOOLEAN_CLASS;}elseif(cls.isAssignableFrom(String.class)){returnPropertyInfo.STRING_CLASS;}elseif(cls.isAssignableFrom(Integer.class)||cls.isAssignableFrom(int.class)||cls.isAssignableFrom(byte.class)||cls.isAssignableFrom(Byte.class)){returnPropertyInfo.INTEGER_CLASS;}elseif(cls.isAssignableFrom(Vector.class)){returnPropertyInfo.VECTOR_CLASS;}elseif(cls.isAssignableFrom(Long.class)||cls.isAssignableFrom(long.class)){returnPropertyInfo.LONG_CLASS;}else{returnPropertyInfo.OBJECT_CLASS;}}}

当然这个类已经基本可以满足大多数不复杂类的调用了。

不过一些嵌套复杂的类型的类仍然可能报序列化的错误,在这里我们将暂时不再深入研究。

有兴趣的可以继续了解一下:

他为什么会报序列化错误?

再writeElement的时候

privatevoidwriteElement(XmlSerializerwriter,Objectelement,PropertyInfotype,Objectmarshal)throwsIOException{if(marshal!=null)((Marshal)marshal).writeInstance(writer,element);elseif(elementinstanceofSoapObject)writeObjectBody(writer,(SoapObject)element);elseif(elementinstanceofKvmSerializable)writeObjectBody(writer,(KvmSerializable)element);elseif(elementinstanceofVector)writeVectorBody(writer,(Vector)element,type.elementType);elsethrownewRuntimeException("Cannotserialize:"+element);}

很显然当他没有Marshal 又不是SoapObject KvmSerializable Vector中的一种类型的时候他就无法序列化了!自然就报错了!那么根据这个我们是不是抓住了点什么?

SoapSerializationEnvelope中有一个这样的addMapping方法Marshal

//他的说明是

//Defines a direct mapping from a namespace and name to a java class (and vice versa)

有兴趣可以研究一下。


好了!基础的对象构建传递就将到这里了!

既然有序列化,那么如何对Ksoap2接收到的服务端数据进行解析呢?敬请期待

下一篇《Android调用WebService系列之KSoap2对象解析》