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.调用是参数的说明
- 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/
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不对应的原因。下面是一个例子
服务端代码:
- 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);//register 这个很重要
- envelope.setOutputSoapObject(requet);
- envelope.dotNet=true;
- AndroidHttpTransport transport=new AndroidHttpTransport (URL);
- ClientUtil.SetCertification(); //设置证书
- try {
- transport.call(SOAP_ACTION,envelope);
- //
- SoapObject response=(SoapObject)envelope.getResponse();
- //
- //PraseXML_SF(response);
- ((TextView)findViewById(R.id.txt01)).setText(String.valueOf(response.toString()));
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (XmlPullParserException e) {
- // TODO Auto-generated catch block
- 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 static final String NAMESPACE = "http://schemas.datacontract.org/2004/07/HL7.Base.Struct";
- 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) {
- // TODO Auto-generated method stub
- switch (index) {
- case 0:
- return UId;
- case 1:
- return UName;
- default:
- return null;
- }
- }
- public int getPropertyCount() {
- // TODO Auto-generated method stub
- return 2;
- }
- public void getPropertyInfo(int index, Hashtable ht, PropertyInfo info) {
- // TODO Auto-generated method stub
- 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) {
- // TODO Auto-generated method stub
- 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) {
- // TODO Auto-generated method stub
- 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里面加入用户信息。
加入的方法是:
- /*
- * authenticator 加入密码账号验证
- * */
- 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对象
Android 调用.Net WCF服务 .的更多相关文章
- Android调用Web服务
现在大部分应用程序都把业务逻辑处理,数据调用等功能封装成了服务的形式,应用程序只需要调用这些web服务就好了,在这里就不赘述web服务的优点了.本文总结如何在android中调用Web服务,通过传递基 ...
- Silverlight中异步调用WCF服务,传入回调函数
以前学的ASP.NET,调用的都是同步方法,同步方法的好处就是,一步一步走,完成这步才会走下一步.然而,WCF使用的都是异步方法,调用之后不管有没有获得结果就直接往下走,最可恶的是异步函数都是Void ...
- 实现jquery.ajax及原生的XMLHttpRequest调用WCF服务的方法
废话不多说,直接讲解实现步骤 一.首先我们需定义支持WEB HTTP方法调用的WCF服务契约及实现服务契约类(重点关注各attribute),代码如下: //IAddService.cs namesp ...
- SharePoint 2013 调用WCF服务简单示例
内容比较简单,主要记录自己使用SharePoint 2013WCF服务遇到的小问题和小经验,分享给大家,希望能够给需要的人有所帮助.好吧,进入正题! 第一部分 SharePoint 2013调用自带W ...
- VS2010中使用Jquery调用Wcf服务读取数据库记录
VS2010中使用Jquery调用Wcf服务读取数据库记录 开发环境:Window Servere 2008 +SQL SERVE 2008 R2+ IIS7 +VS2010+Jquery1.3.2 ...
- Wcf for wp8 创建wcf服务 连接wp8模拟器并显示来自wcf服务的接口信息 (一)
下载: vs2012 pro for wp8 iis express http://download.microsoft.com/download/B/2/8/B2801FEE-9A60-4AFA-8 ...
- WCF学习之旅—WCF服务部署到IIS7.5(九)
上接 WCF学习之旅—WCF寄宿前的准备(八) 四.WCF服务部署到IIS7.5 我们把WCF寄宿在IIS之上,在IIS中宿主一个服务的主要优点是在发生客户端请求时宿主进程会被自动启动,并且你可以 ...
- WCF学习之旅—WCF服务配置(十四)
一.概述 我们在前面章节中讲了寄宿,在前面的实例中也用到了配置文件,这一篇主要讲讲如何在应用配置文件,提高WCF程序的灵活性.在编写WCF服务应用程序时,编写配置项也是其中一项主要工作,在前面的几个示 ...
- WCF服务二:创建一个简单的WCF服务程序
在本例中,我们将实现一个简单的计算服务,提供基本的加.减.乘.除运算,通过客户端和服务端运行在同一台机器上的不同进程实现. 一.新建WCF服务 1.新建一个空白解决方案,解决方案名称为"WC ...
随机推荐
- CentOS7.x安装Java
1.在甲骨文注册帐号下载需要版本的java的源码包 https://www.oracle.com/java/technologies/jdk12-downloads.html 2.把下载好的jdk的源 ...
- helm笔记
一.注意事项 1.values.yaml 中可以使用'#'号注释行,而/templates 下的文件不能用#号,如果要注释可以使用 {{/* context */}} 2.{{- #忽略 ...
- java安全相关知识
基本概念 JVM:java虚拟机,Java编译程序将生成Java虚拟机上可运行的目标代码,使得Java程序可以再不同平台不加修改的运行.JVM包含完善的硬件架构,主要分为五大模块-类装载器子系统.运行 ...
- python_并发编程——管道
1.管道 from multiprocessing import Pipe conn1,conn2 = Pipe() #返回两个值 conn1.send('wdc') #发送 print(conn2. ...
- strtol函数的用法——字符串转长整形
/* strtol example */ #include <stdio.h> /* printf */ #include <stdlib.h> /* strtol */ in ...
- Dubbo源码分析(2):ServiceBean
ServiceBean时序图
- C++第三章课后作业答案及解析---指针的使用
今天继续完成上周没有完成的习题---C++第三章课后作业,本章题涉及指针的使用,有指向对象的指针做函数参数,对象的引用以及友元类的使用方法等 它们具体的使用方法在下面的题目中会有具体的解析(解析标注在 ...
- The 2018 ACM-ICPC CCPC 宁夏 A-Maximum Element In A Stack
题意 对一个栈有入栈和出栈两种操作,求每次操作后栈的最大值的异或. 题目链接 分析 类似于单调栈,但是还没有那么复杂. 只需保持栈顶为最大值,如果入栈元素小于栈顶元素,则重复一次栈顶元素入栈:否则,直 ...
- sql server 中的表值函数和标量值函数
顾名思义:表值函数返回的是表,而标量值函数可以返回基类型 一.表值函数 用户定义表值函数返回 table 数据类型.对于内联表值函数,没有函数主体:表是单个 SELECT 语句的结果集. 以下示例 ...
- 2019.11.15 JQ图片轮播
<div class="three"> <div class="bjtp"> <img class="bjpic b1& ...