Android 调用.Net WCF服务

本来以为在java平台上用axis2生成了客户端代理类然后移植到Android平台上就好了。没想到在移植过程中出现了很多问题。说明JVM和android的DVM差距还是很大的。

JVM执行的是class文件,而DVM执行的是dex文件。

在eclipse里面开发Android程序的时候在编译时会把jar包里面的class一个个编译成DVM可执行的dex文件。当然,有个前提是jar包是放在source folder里面的。这样eclipse才会在编译程序的时候将jar包编译到apk文件中去。要不然虽然本地eclipse不会报错,但是在模拟器中会报错NoClassDefFound。

而且有的jar包是不能被dexdump.exe正确转换成dex文件的。这样就导致这个jar包不能用,后果是整个程序都不能正确运行。

我在将axis2移植到Android平台上去的时候有一些jar包转换不了。然后网上找了很多资料,都没人解决这个问题。希望如果有人解决了能共享一下下。

后来实在不行了,看网上说在Android平台都用ksoap2来调用Web Service。自己觉得解决不了axis2的问题。于是只能改变方向。学习了一下ksoap2。在ksoap2调用WCF服务的时候也出现了很多问题。好在后来慢慢都解决了。现在将我遇到的问题和解决的方案都写下来,供其他也碰到这些问题的人参考。

 

下面列举一下我碰到的问题和解决方案

1.调用是参数的说明

  1. static String NameSpace="http://tempuri.org/"; 
  2. static String URL="https://10.0.2.2:9001/test"; 
  3. static String SOAP_ACTION="http://tempuri.org/ITestService/GetUser"; 
  4. static String MethodName="GetUser"; 

 

Namespace 是你设置的服务命名空间,一般没有设置就是http://tempuri.org/

URL是你服务暴露的地址,通过这个地址可以获取wsdl。在android里面127.0.0.1代表的是模拟器的地址,而10.0.0.2代表的才是电脑的127.0.0.1。所以如果是自己本机做WCF服务器的话,程序里面应该这么设置。

SOAP_ACTION是你的wsdl里面相对应的方法的地址。

MethodName就是SOAP_ACTION最后面的那个指明ACTION的方法名。

 

2.参数传递 复杂对象

服务里面不可避免的是会传递参数,但是在可能在wcf服务端可能解析不了你传的参数。通过tcptrace截取soap后发现是参数的namespace不对应的原因。下面是一个例子

服务端代码:

  1. User ITestService.GetUser(User u) 
  2.         { 
  3.             User user = new User(); 
  4.             user.UId = "Server:" + u.UId; 
  5.             user.UName = "Server:" + u.UName; 
  6.             return user; 
  7.         } 

 

User类:

  1. [DataContract] 
  2.     publicclass User 
  3.     { 
  4.         [DataMember] 
  5.         publicstring UId; 
  6.         [DataMember] 
  7.         publicstring UName; 
  8.     } 

 

 

android客户端代码如下:

  1. SoapObject requet=new SoapObject(NameSpace,MethodName); 
  2.          
  3.         PropertyInfo perPropertyInfo=new PropertyInfo(); 
  4.         User user=new User(); 
  5.         user.setUId("123"); 
  6.         user.setUName("cch"); 
  7.         perPropertyInfo.setName("u"); 
  8.         perPropertyInfo.setValue(user); 
  9.         perPropertyInfo.setType(User.class); 
  10.         requet.addProperty(perPropertyInfo); 
  11.          
  12.         SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11); 
  13.         envelope.addMapping(User.NAMESPACE,"User",User.class);//register 这个很重要 
  14.         envelope.setOutputSoapObject(requet); 
  15.         envelope.dotNet=true; 
  16.         AndroidHttpTransport transport=new AndroidHttpTransport (URL); 
  17.          
  18.         ClientUtil.SetCertification();  //设置证书 
  19.         try { 
  20.              
  21.             transport.call(SOAP_ACTION,envelope); 
  22. //           
  23.             SoapObject response=(SoapObject)envelope.getResponse(); 
  24. //           
  25.             //PraseXML_SF(response); 
  26.             ((TextView)findViewById(R.id.txt01)).setText(String.valueOf(response.toString())); 
  27.         } catch (IOException e) { 
  28.             // TODO Auto-generated catch block 
  29.             e.printStackTrace(); 
  30.         } catch (XmlPullParserException e) { 
  31.             // TODO Auto-generated catch block 
  32.             e.printStackTrace(); 
  33.         } 

 

android端也有一个User类,这个类是继承的BaseObject,BaseObject实现KvmSerializable接口

先BaseObject:

  1. package CCH.Model; 
  2. import org.ksoap2.serialization.KvmSerializable; 
  3. import org.ksoap2.serialization.SoapObject; 
  4. publicabstractclass BaseObject implements KvmSerializable 
  5. { 
  6.     publicstaticfinal String NAMESPACE = "http://schemas.datacontract.org/2004/07/TestService"; 
  7.     //public static final String NAMESPACE = "http://schemas.datacontract.org/2004/07/HL7.Base.Struct"; 
  8.         public BaseObject() { 
  9.             super(); 
  10.         } 
  11.      
  12. } 

 

然后是User类

  1. package CCH.Model; 
  2. import java.util.Hashtable; 
  3. import org.ksoap2.serialization.PropertyInfo; 
  4. publicclass User extends BaseObject 
  5. { 
  6.     private String UId; 
  7.     private String UName; 
  8.      
  9.     public Object getProperty(int index) { 
  10.         // TODO Auto-generated method stub 
  11.         switch (index) { 
  12.         case0: 
  13.             return UId; 
  14.         case1: 
  15.             return UName; 
  16.         default: 
  17.             returnnull; 
  18.         } 
  19.     } 
  20.     publicint getPropertyCount() { 
  21.         // TODO Auto-generated method stub 
  22.         return2; 
  23.     } 
  24.     publicvoid getPropertyInfo(int index, Hashtable ht, PropertyInfo info) { 
  25.         // TODO Auto-generated method stub 
  26.         info.namespace=super.NAMESPACE;//这个很重要 
  27.         switch (index) { 
  28.         case0: 
  29.             info.type=PropertyInfo.STRING_CLASS; 
  30.             info.name="UId"; 
  31.             break; 
  32.         case1: 
  33.             info.type=PropertyInfo.STRING_CLASS; 
  34.             info.name="UName"; 
  35.             break; 
  36.         default: 
  37.             break; 
  38.         } 
  39.     } 
  40.     publicvoid setProperty(int index, Object value) { 
  41.         // TODO Auto-generated method stub 
  42.         switch (index) { 
  43.         case0: 
  44.             UId=value.toString(); 
  45.             break; 
  46.         case1: 
  47.             UName=value.toString(); 
  48.             break; 
  49.         default: 
  50.             break; 
  51.         } 
  52.     } 
  53.     public String getUId() { 
  54.         return UId; 
  55.     } 
  56.     publicvoid setUId(String uId) { 
  57.         UId = uId; 
  58.     } 
  59.     public String getUName() { 
  60.         return UName; 
  61.     } 
  62.     publicvoid setUName(String uName) { 
  63.         UName = uName; 
  64.     } 
  65.      
  66. } 

 

因为要序列化啊什么什么的,解释起来比较烦。这边也不解释了。大家有兴趣可以去查一下。只说明一下是通过info.namespace+info.name来反序列化的。

 

3.如果有证书加密,会一直说timeout。

解决方法是在android客户端调用下面这个方法。这个方法要在httptransport.call()之前调用

 

  1. ClientUtil.SetCertification();  //设置证书 

 

类是这么写的:

  1. publicclass ClientUtil { 
  2.     //设置证书被信任 
  3.     publicstaticvoid SetCertification() { 
  4.         try { 
  5.             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){ 
  6.                     @Override 
  7.                     publicboolean verify(String hostname, 
  8.                             SSLSession session) { 
  9.                         // TODO Auto-generated method stub 
  10.                         returntrue; 
  11.                     }}); 
  12.             SSLContext context = SSLContext.getInstance("TLS"); 
  13.             context.init(null, new X509TrustManager[]{new X509TrustManager(){ 
  14.                     publicvoid checkClientTrusted(X509Certificate[] chain, 
  15.                                     String authType) throws CertificateException {} 
  16.                     publicvoid checkServerTrusted(X509Certificate[] chain, 
  17.                                     String authType) throws CertificateException {} 
  18.                     public X509Certificate[] getAcceptedIssuers() { 
  19.                             returnnew X509Certificate[0]; 
  20.                     }}}, new SecureRandom()); 
  21.             HttpsURLConnection.setDefaultSSLSocketFactory( 
  22.                             context.getSocketFactory()); 
  23.     } catch (Exception e) {  
  24.             e.printStackTrace(); 
  25.     } 
  26. } 
  27. } 

 

这个信任一切证书。应为自制的证书是不被信任的,所以shakehand的时候一直timeout。

4.wcf设置的自定义帐号密码认证(userNameAuthentication  )

加入这个之后要在soap发送前在head里面加入用户信息。

加入的方法是:

  1. /*
  2.          * authenticator 加入密码账号验证
  3.          * */ 
  4.         ServiceConnection connection=super.getServiceConnection(); 
  5.         String Login=Base64.encode("cch:cch1".getBytes()); 
  6.         connection.setRequestProperty("Authorization","Basic "+Login); 

 

这个需要重写httptransport的getServiceConnection()方法。

因为在调用httptransport.call()的时候Connection才被初始化,所以在程序外getServiceConnection().setRequestProperty()会报错说nullpoint。

 

希望对大家有所帮助。

 

贴一下解析代码:

  1. int resultCount=response.getPropertyCount(); 
  2.         ArrayList<Dy_sdzbh> list=new ArrayList<Dy_sdzbh>(); 
  3.         for (int i = 0; i < resultCount; i++) { 
  4.             SoapObject item = (SoapObject)response.getProperty(i); 
  5.             String sdzbh = item.getProperty("Sdzbh").toString(); 
  6.             String sfm = item.getProperty("Sfm").toString(); 
  7.             Dy_sdzbh modelDySdzbh=new Dy_sdzbh(); 
  8.             modelDySdzbh.setSfdzbm(sdzbh); 
  9.             modelDySdzbh.setSfm(sfm); 
  10.             list.add(modelDySdzbh); 
  11.         } 
  12.         for (int i = 0; i < resultCount; i++) { 
  13.             java.lang.System.out.println(list.get(i).getSfm()); 
  14.         } 
posted @ 2012-08-11 13:57  不弃的追求  阅读(1688)  评论(1)    收藏  举报