Android 調用.Net WCF服務 .

原本覺得在java平臺上用axis2生成了客戶端代理類而後移植到Android平臺上就行了。沒想到在移植過程當中出現了不少問題。說明JVM和android的DVM差距仍是很大的。html

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

在eclipse裏面開發Android程序的時候在編譯時會把jar包裏面的class一個個編譯成DVM可執行的dex文件。固然,有個前提是jar包是放在source folder裏面的。這樣eclipse纔會在編譯程序的時候將jar包編譯到apk文件中去。要否則雖然本地eclipse不會報錯,可是在模擬器中會報錯NoClassDefFound。android

並且有的jar包是不能被dexdump.exe正確轉換成dex文件的。這樣就致使這個jar包不能用,後果是整個程序都不能正確運行。數組

我在將axis2移植到Android平臺上去的時候有一些jar包轉換不了。而後網上找了不少資料,都沒人解決這個問題。但願若是有人解決了能共享一下下。服務器

後來實在不行了,看網上說在Android平臺都用ksoap2來調用Web Service。本身以爲解決不了axis2的問題。因而只能改變方向。學習了一下ksoap2。在ksoap2調用WCF服務的時候也出現了不少問題。好在後來慢慢都解決了。如今將我遇到的問題和解決的方案都寫下來,供其餘也碰到這些問題的人蔘考。session

 

下面列舉一下我碰到的問題和解決方案app

1.調用是參數的說明dom

 

  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/eclipse

URL是你服務暴露的地址,經過這個地址能夠獲取wsdl。在android裏面127.0.0.1表明的是模擬器的地址,而10.0.0.2表明的纔是電腦的127.0.0.1。因此若是是本身本機作WCF服務器的話,程序裏面應該這麼設置。tcp

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對象

相關文章
相關標籤/搜索