原本覺得在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
- static String NameSpace="http://tempuri.org/";
- static String URL="https://10.0.2.2:9001/test";
- static String SOAP_ACTION="http://tempuri.org/ITestService/GetUser";
- 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不對應的緣由。下面是一個例子
服務端代碼:
- User ITestService.GetUser(User u)
- {
- User user = new User();
- user.UId = "Server:" + u.UId;
- user.UName = "Server:" + u.UName;
- return user;
- }
User類:
- [DataContract]
- public class User
- {
- [DataMember]
- public string UId;
- [DataMember]
- public string UName;
- }
android客戶端代碼以下:
- SoapObject requet=new SoapObject(NameSpace,MethodName);
-
- PropertyInfo perPropertyInfo=new PropertyInfo();
- User user=new User();
- user.setUId("123");
- user.setUName("cch");
- perPropertyInfo.setName("u");
- perPropertyInfo.setValue(user);
- perPropertyInfo.setType(User.class);
- requet.addProperty(perPropertyInfo);
-
- SoapSerializationEnvelope envelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);
- envelope.addMapping(User.NAMESPACE,"User",User.class);
- envelope.setOutputSoapObject(requet);
- envelope.dotNet=true;
- AndroidHttpTransport transport=new AndroidHttpTransport (URL);
-
- ClientUtil.SetCertification();
- try {
-
- transport.call(SOAP_ACTION,envelope);
- SoapObject response=(SoapObject)envelope.getResponse();
-
- ((TextView)findViewById(R.id.txt01)).setText(String.valueOf(response.toString()));
- } catch (IOException e) {
-
- e.printStackTrace();
- } catch (XmlPullParserException e) {
-
- e.printStackTrace();
- }
android端也有一個User類,這個類是繼承的BaseObject,BaseObject實現KvmSerializable接口
先BaseObject:
- package CCH.Model;
- import org.ksoap2.serialization.KvmSerializable;
- import org.ksoap2.serialization.SoapObject;
- public abstract class BaseObject implements KvmSerializable
- {
- public static final String NAMESPACE = "http://schemas.datacontract.org/2004/07/TestService";
-
- public BaseObject() {
- super();
- }
-
- }
而後是User類
- package CCH.Model;
- import java.util.Hashtable;
- import org.ksoap2.serialization.PropertyInfo;
- public class User extends BaseObject
- {
- private String UId;
- private String UName;
-
- public Object getProperty(int index) {
-
- switch (index) {
- case 0:
- return UId;
- case 1:
- return UName;
- default:
- return null;
- }
- }
- public int getPropertyCount() {
-
- return 2;
- }
- public void getPropertyInfo(int index, Hashtable ht, PropertyInfo info) {
-
- info.namespace=super.NAMESPACE;
- switch (index) {
- case 0:
- info.type=PropertyInfo.STRING_CLASS;
- info.name="UId";
- break;
- case 1:
- info.type=PropertyInfo.STRING_CLASS;
- info.name="UName";
- break;
- default:
- break;
- }
- }
- public void setProperty(int index, Object value) {
-
- switch (index) {
- case 0:
- UId=value.toString();
- break;
- case 1:
- UName=value.toString();
- break;
- default:
- break;
- }
- }
- public String getUId() {
- return UId;
- }
- public void setUId(String uId) {
- UId = uId;
- }
- public String getUName() {
- return UName;
- }
- public void setUName(String uName) {
- UName = uName;
- }
-
- }
由於要序列化啊什麼什麼的,解釋起來比較煩。這邊也不解釋了。你們有興趣能夠去查一下。只說明一下是經過info.namespace+info.name來反序列化的。
3.若是有證書加密,會一直說timeout。
解決方法是在android客戶端調用下面這個方法。這個方法要在httptransport.call()以前調用
- ClientUtil.SetCertification();
類是這麼寫的:
- public class ClientUtil {
-
- public static void SetCertification() {
- try {
- HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
- @Override
- public boolean verify(String hostname,
- SSLSession session) {
-
- return true;
- }});
- SSLContext context = SSLContext.getInstance("TLS");
- context.init(null, new X509TrustManager[]{new X509TrustManager(){
- public void checkClientTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {}
- public void checkServerTrusted(X509Certificate[] chain,
- String authType) throws CertificateException {}
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }}}, new SecureRandom());
- HttpsURLConnection.setDefaultSSLSocketFactory(
- context.getSocketFactory());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
這個信任一切證書。應爲自制的證書是不被信任的,因此shakehand的時候一直timeout。
4.wcf設置的自定義賬號密碼認證(userNameAuthentication )
加入這個以後要在soap發送前在head裏面加入用戶信息。
加入的方法是:
- ServiceConnection connection=super.getServiceConnection();
- String Login=Base64.encode("cch:cch1".getBytes());
- connection.setRequestProperty("Authorization","Basic "+Login);
這個須要重寫httptransport的getServiceConnection()方法。
由於在調用httptransport.call()的時候Connection才被初始化,因此在程序外getServiceConnection().setRequestProperty()會報錯說nullpoint。
但願對你們有所幫助。
貼一下解析代碼:
- int resultCount=response.getPropertyCount();
- ArrayList<Dy_sdzbh> list=new ArrayList<Dy_sdzbh>();
- for (int i = 0; i < resultCount; i++) {
- SoapObject item = (SoapObject)response.getProperty(i);
- String sdzbh = item.getProperty("Sdzbh").toString();
- String sfm = item.getProperty("Sfm").toString();
- Dy_sdzbh modelDySdzbh=new Dy_sdzbh();
- modelDySdzbh.setSfdzbm(sdzbh);
- modelDySdzbh.setSfm(sfm);
- list.add(modelDySdzbh);
- }
- for (int i = 0; i < resultCount; i++) {
- java.lang.System.out.println(list.get(i).getSfm());
- }
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對象