平时在开发中,都是使用http协议发送get或post方法,得到一个json的字符串,然后解析封装。
但是今天遇到一个项目,是使用android去调用WebService接口来获取数据,得到的是一个xml的字符串并对其进行封装。这里就遇到2个问题。
1、android上如何去调用一个webService? 网络上关于这里的资料也比较多,但是要自己真正开发才能了解整个流程。这里我参考了以下几篇文章
(1)、http://www.cnblogs.com/shenliang123/archive/2012/07/05/2578586.html
这篇文章从整体流程,介绍了如何android上如何调用webService,之前不了解什么是webService或者是android开发者刚接触调用webService的同学,都可以看看。
(2)、http://blog.csdn.net/xiaanming/article/details/16871117
这篇文章最大的亮点是使用了一个自定义的封装工具类,对调用webService进行了很好的封装,以后调用就变得相对简单了。但是博文提供的一个Demo,最后查看某个城市具体天气无法查询。所以最好还是自己写一次,加深理解。
(3)、http://blog.csdn.net/menhin/article/details/11955207
http://bbs.csdn.net/topics/350047287
http://blog.csdn.net/lyq8479/article/details/6428288(11月27日更新另外一个参考文章)
以上文章是开发中遇到的一些问题,有需要的同学可以参考下
2、xml的数据解析封装其实相比json,是复杂不少的,一般常用的是用到pull解析。而且要更具xml内容不同写不同的解析方法,其实很繁琐。
接下来整个过程遇到了不少问题,也有不少总结,结合上面的博文和自己理解在这里写下来我的一些理解。
首先,android上究竟如何调用webService呢,整个流程如下:
1、先使用一个叫ksoap2的jar包,最新的是3.0版,把它放入lib包下即可,点击我下载,注意检查eclipse中的android private libraries中是否有编译通过,如有下图就表示成功。
2、创建HttpTransportsSE对象。通过HttpTransportsSE类的构造方法可以指定WebService的WSDL文档的URL,也就是webService的访问地址。
HttpTransportSE httpTransportSE = new HttpTransportSE(url);
3、创建SoapObject对象,里面的参数分别是WebService的命名空间和调用方法名。
SoapObject soapObject = new SoapObject(NAMESPACE, methodName);
4、设置调用方法的参数值,如果没有参数,就不设置,有参数的话调用SoapObject对象的addProperty(String name, Object value)方法将参数加入到SoapObject对象中。(注意:设置参数时,参数名不一定需要跟调用的服务器端的参数名相同,只需要对应的顺序相同即可)
soapObject.addProperty(“参数名字”, “传递的参数值”);
5、实例化SoapSerializationEnvelope,传入WebService的SOAP协议的版本号。(可以比服务器的版本更低,但是不能更高。比如最后的VER11为VER12就会出错。这里要跟服务端开发的沟通,确定soap的版本号是多少)
SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
6、通过setOutputSoapObject(Object soapObject)将上面第3步创建的SoapObject对象设置到里面,并设置是否调用的是.Net开发的WebService和是否debug等信息。
soapEnvelope.setOutputSoapObject(soapObject);
soapEnvelope.dotNet = true;
httpTransportSE.debug = true;
7、我们在线程中调用HttpTransportsSE对象的call方法就能实现对WebService的调用,其中,第一个参数为命名空间+调用方法,第二个参数为第5步建立的SoapSerializationEnvelope对象(因为涉及到网络操作,所以使用了线程池来异步操作调用WebService接。)
httpTransportSE.call(NAMESPACE + methodName, soapEnvelope);
8、通过soapEnvelope.bodyIn获取WebService返回的信息。返回对象是SoapObject类型,该对象就代表Webservice的返回消息,析该对象,即可获得调用web service的返回值。
SoapObject resultSoapObject = (SoapObject) soapEnvelope.bodyIn;
讲了这么多,说说具体怎么使用,下面的例子用了第2篇博客的一个工具类
现在我有一个webService,访问地址是
http://192.168.1.101:8080/webservice/LoginWebservice?wsd
里面有一个方法getLoginXml,参数的类型是String字符串,参数的名字inputXmlString
该方法的功能是,你随便传递一个字符串,服务端会打印你字符串,并返回下面xml格式的字符串
package com.example.webservice;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.http.client.HttpResponseException;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;
import android.os.Handler;
import android.os.Message;
/**
* 访问WebService的工具类,使用了线程池
*
* @see http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*/
public class WebServiceUtils {
//下面这个是本机的服务器地址,使用前需要先部署
//注意,这里的url地址,如果有?wsdl,必须去掉!!!!比如下面的第2个地址就不行,会出现异常org.xmlpull.v1.XmlPullParserException: expected: START_TAG
public static final String WEB_SERVER_URL = "http://192.168.1.101:8080/webservice/LoginWebservice";
//public static final String WEB_SERVER_URL = "http://192.168.1.101:8080/webservice/LoginWebservice?wsdl";
// 含有3个线程的线程池
private static final ExecutorService executorService = Executors
.newFixedThreadPool(3);
// 命名空间 ,注意有的最后带一个反斜杠,有的没有,这里要查看wsdl文档,具体设置。可以比较如下
//private static final String NAMESPACE = "http://WebXml.com.cn/";、
private static final String NAMESPACE = "http://webservice";
/**
*
* @param url
* WebService服务器地址
* @param methodName
* WebService的调用方法名
* @param properties
* WebService的参数
* @param webServiceCallBack
* 回调接口,这个回调接口是自己写的
*/
public static void callWebService(String url, final String methodName,
HashMap<String, String> properties,
final WebServiceCallBack webServiceCallBack) {
//第1步:先使用一个叫ksoap2的jar包,最新的是3.0版,把它放入lib包下即可
// 第2步 : 创建HttpTransportSE对象,传递WebService服务器地址
final HttpTransportSE httpTransportSE = new HttpTransportSE(url);
// 第3步:创建SoapObject对象
SoapObject soapObject = new SoapObject(NAMESPACE, methodName);
//第4步:如果有参数传给Web Service服务器端,调用SoapObject对象的addProperty(String name, Object value)方法来设置参数,该方法的name参数指定参数名
// SoapObject添加参数
if (properties != null) {
for (Iterator<Map.Entry<String, String>> it = properties.entrySet()
.iterator(); it.hasNext();) {
Map.Entry<String, String> entry = it.next();
//下面这一句就是给服务端传递数据, 分别是参数的名字和 参数的值
//注意:设置参数,参数名不一定需要跟调用的服务器端的参数名相同,只需要对应的顺序相同即可!!!!!!!!
soapObject.addProperty(entry.getKey(), entry.getValue());
}
}
// 第5步 :实例化SoapSerializationEnvelope,传入WebService的SOAP协议的版本号
//可以比服务器的版本更低,但是不能更高。比如最后的VER11为VER12就会出错。这里要跟服务端开发的沟通,确定soap的版本号是多少
final SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
//第6步:将上面的第3步建立的SoapObject对象通过setOutputSoapObject(Object soapObject)设置到里面,并设置是否调用的是.Net开发的WebService和是否debug等信息
soapEnvelope.setOutputSoapObject(soapObject);
// 设置是否调用的是.Net开发的WebService
soapEnvelope.dotNet = true;
httpTransportSE.debug = true;
// 用于子线程与主线程通信的Handler
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 将返回值回调到callBack的参数中
//SoapObject就是服务端返回的参数,需要解析它即可
webServiceCallBack.callBack((SoapObject) msg.obj);
}
};
// 开启线程去访问WebService
executorService.submit(new Runnable() {
@Override
public void run() {
SoapObject resultSoapObject = null;
try {
//第7步:我们在线程中调用HttpTransportsSE对象的call方法就能实现对WebService的调用,其中,第一个参数为命名空间+调用方法,第二个参数为第5步建立的SoapSerializationEnvelope对象(因为涉及到网络操作,所以使用了线程池来异步操作调用WebService接。)
httpTransportSE.call(NAMESPACE + methodName, soapEnvelope);
if (soapEnvelope.getResponse() != null) {
// 第8步:通过soapEnvelope.bodyIn,获取服务器响应返回的SoapObject,该对象就代表Webservice的返回消息,
//解析该对象,即可获得调用web service的返回值
resultSoapObject = (SoapObject) soapEnvelope.bodyIn;
}
} catch (HttpResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} finally {
// 将获取的消息利用Handler发送到主线程
mHandler.sendMessage(mHandler.obtainMessage(0,
resultSoapObject));
}
}
});
}
/**
*
*
* @author xiaanming
*
*/
public interface WebServiceCallBack {
public void callBack(SoapObject result);
}
}
package com.example.webservice;
import java.util.HashMap;
import org.ksoap2.serialization.SoapObject;
import com.example.mydemo.R;
import com.example.webservice.WebServiceUtils.WebServiceCallBack;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
/**
* 通过这个来实现
* @author hemiy
* @copyright
*/
public class TestWebservice extends Activity {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_webservice);
text=(TextView) findViewById(R.id.content);
}
public void testWeb(View view){
//显示进度条
ProgressDialogUtils.showProgressDialog(this, "数据加载中...");
//添加参数
HashMap<String, String> properties = new HashMap<String, String>();
//其实这里的key可以为任意字符串,但是不能为数字的字符串
// 比如 properties.put("1", "这里是自己封装好上传的xml格式数据");
// 会报异常 java.io.IOException: HTTP request failed, HTTP status: 500
properties.put("h", "这里是自己封装好上传的xml格式字符串数据");
WebServiceUtils.callWebService(WebServiceUtils.WEB_SERVER_URL, "getLoginXml", properties, new WebServiceCallBack() {
//调用返回的数据执行如下的代码
public void callBack(SoapObject result) {
ProgressDialogUtils.dismissProgressDialog();
if(result != null){
String name = result.getProperty(0).toString();
text.setText(name);
}else{
Toast.makeText(TestWebservice.this, "获取数据错误", Toast.LENGTH_SHORT).show();
}
}
});
}
}
tomcat服务器的打印结果,表示成功。
开发中遇到问题
1、android连接webservice报java.io.Exception: Http request failed,Http status 500 tran.call出错。
http 500错误是服务器内部错误,确认你是否安装接口指定参数格式提交或者与服务器端沟通确认。
2、注意命名空间是否带下划线;
3、注意传递的参数“名字”是否正确,有的服务端要求名字一定要指定的,如天气预报。而自己的服务器除了数字字符串,其实可以任意取名
4、这里的服务器url地址,如果有?wsdl,必须去掉!!!!
比如下面的地址就不行,会出现异常org.xmlpull.v1.XmlPullParserException: expected: START_TAG
public static final String WEB_SERVER_URL = "http://192.168.1.101:8080/webservice/LoginWebservice?wsdl";
5、一些参考的网址(12月7日更新)
http://bbs.csdn.net/topics/390788599
这个是一个常见的报错,原因是服务器回传的不是xml,不能用ksoap2必须使用http发送post请求
http://stackoverflow.com/questions/tagged/android-ksoap2?sort=frequent&pageSize=15
http://blog.csdn.net/menhin/article/details/11955207
以上是一些错误异常分析
http://www.cnblogs.com/gzggyy/archive/2011/06/21/2086159.html
http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/10/3072216.html
以上是如何使用http去调用服务端的例子
http://blog.csdn.net/pfe_nova/article/details/26147297
http://bbs.csdn.net/topics/390615324
http://www.cnblogs.com/shenliang123/archive/2012/07/05/2578586.html
http://blog.csdn.net/x605940745/article/details/16834127
以上是查看的文章
http://webservice.36wu.com/mapService.asmx
http://blog.csdn.net/lsvtogergo/article/details/21445783
常用的webservice服务