• 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.     public class User  
    3.     {  
    4.         [DataMember]  
    5.         public string UId;  
    6.         [DataMember]  
    7.         public string 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. public abstract class BaseObject implements KvmSerializable  
    5. {  
    6.     public static final 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. public class 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.         case 0:  
    13.             return UId;  
    14.         case 1:  
    15.             return UName;  
    16.         default:  
    17.             return null;  
    18.         }  
    19.     }  
    20.     public int getPropertyCount() {  
    21.         // TODO Auto-generated method stub   
    22.         return 2;  
    23.     }  
    24.     public void getPropertyInfo(int index, Hashtable ht, PropertyInfo info) {  
    25.         // TODO Auto-generated method stub   
    26.         info.namespace=super.NAMESPACE;//这个很重要   
    27.         switch (index) {  
    28.         case 0:  
    29.             info.type=PropertyInfo.STRING_CLASS;  
    30.             info.name="UId";  
    31.             break;  
    32.         case 1:  
    33.             info.type=PropertyInfo.STRING_CLASS;  
    34.             info.name="UName";  
    35.             break;  
    36.         default:  
    37.             break;  
    38.         }  
    39.     }  
    40.     public void setProperty(int index, Object value) {  
    41.         // TODO Auto-generated method stub   
    42.         switch (index) {  
    43.         case 0:  
    44.             UId=value.toString();  
    45.             break;  
    46.         case 1:  
    47.             UName=value.toString();  
    48.             break;  
    49.         default:  
    50.             break;  
    51.         }  
    52.     }  
    53.     public String getUId() {  
    54.         return UId;  
    55.     }  
    56.     public void setUId(String uId) {  
    57.         UId = uId;  
    58.     }  
    59.     public String getUName() {  
    60.         return UName;  
    61.     }  
    62.     public void setUName(String uName) {  
    63.         UName = uName;  
    64.     }  
    65.       
    66. }  

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

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

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

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

    类是这么写的:

    1. public class ClientUtil {  
    2.     //设置证书被信任   
    3.     public static void SetCertification() {  
    4.         try {  
    5.             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){  
    6.                     @Override  
    7.                     public boolean verify(String hostname,  
    8.                             SSLSession session) {  
    9.                         // TODO Auto-generated method stub   
    10.                         return true;  
    11.                     }});  
    12.             SSLContext context = SSLContext.getInstance("TLS");  
    13.             context.init(null, new X509TrustManager[]{new X509TrustManager(){  
    14.                     public void checkClientTrusted(X509Certificate[] chain,  
    15.                                     String authType) throws CertificateException {}  
    16.                     public void checkServerTrusted(X509Certificate[] chain,  
    17.                                     String authType) throws CertificateException {}  
    18.                     public X509Certificate[] getAcceptedIssuers() {  
    19.                             return new 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.         }  

    3 SoapObject 解析
              SoapObject  soapChild=(SoapObject)result.getProperty(int);

              For(int i=0; i<soapChild.getPropertyCount();i++){

                        SoapObject  soapChilds=(SoapObject)result.getProperty(i);

                        String data=soapChilds.getProperty(“Key_Name”).toString();

    }

    SoapObject类是一个主要用于调用WCF服务的类,其对象可以作为请求,发送到WCF服务器;也可以用于存储响应信息。

    该对象本身是一个存储了一套HTML语句的文本。而SoapObject本身提供了对这套HTML语句的解析。


    我们对SoapObject的解析,其实可以理解为对HTML语句的解析。

    本文以String类型为获取目标(即从WCF服务器提供方法返回的是String类型的数据)

    首先我们把返回的String类型分成三种情况:单一个String, 一个String的数组,一个String的二维数组

    ①对于单一个String,我们在编写调用WCF服务的方法的时候,envelope.bodyIn就不能强制转换成SoapObject,否则会在运行时提示类型转换错误。此时envelope.bodyIn可以直接作为一个Object对象返回,也可以调用其toString()方法,即可获得想要的数据。

    ②对于一个String的一维数组,我们要把envelope.bodyIn强制转换成SoapObject,获取一个SoapObject类型的对象soap。

    此时,只要调用SoapObject的getProperty()方法即可获得想要的数据,参数对应一维数组下标。

    例如:要从返回一维数组获取第一个元素,只要调用soap.setProperty(0)即可。

    ③对于一个String的二维数组。

    此时。通过调试发现,getProgerty()方法返回的是另一个SoapObject对象,因此我们可以把envelope.bodyIn的SoapObject对象想象成一个二维数组,其中getProperty()方法是返回一维数组的某一行,参数是对应行下标,再通过调用这一个SoapObject的getProperty()方法,即可获取某一元素。

    例如:要获取String[0][1]元素,只要从envelope.bodyIn的SoapObject调用两次getProperty()方法:soap.getProperty(0).getProperty(1)

    可见,只要把SoapObject抽象成一个数组,就不难去理解和解析其中的数据。

    把SoapObject想象成一个数组,这个数组当中的元素可以是他自己,soapObject.setProperty(int index, Object value),此处的value也可以是一个soapObject对象

  • 相关阅读:
    MFC记录
    【转】linux下tty,控制台,虚拟终端,串口,console(控制台终端)详解----不错
    【转】五大绝招复制不能复制的网页文字
    【转】DELL戴尔N4050笔记本拆机(图文)
    【转】Linux下tar.xz结尾的文件的解压方法
    【转】在Ubuntu 12.04 上为Virtualbox 启用USB 设备支持--不错
    【转】Android adb shell操作时出现“ XXX ... Read-only file system”解决办法--不错
    【转】VirtualBox direct access to SD Card in Windows--不错
    网址
    TensorFlow 学习(九)—— 初始化函数(概率分布函数 api、常数生成函数)
  • 原文地址:https://www.cnblogs.com/Alex80/p/11111896.html
一二三 - 开发者的网上家园