現在大部分應用程式都把業務邏輯處理,數據調用等功能封裝成了服務的形式,應用程式只需要調用這些web服務就好了,在這裡就不贅述web服務的優點了。本文總結如何在android中調用Web服務,通過傳遞基類型和複雜類型對比調用.NET平臺發佈的WCF服務和WebService服務之間的區別。 0 寫在前 ...
現在大部分應用程式都把業務邏輯處理,數據調用等功能封裝成了服務的形式,應用程式只需要調用這些web服務就好了,在這裡就不贅述web服務的優點了。本文總結如何在android中調用Web服務,通過傳遞基類型和複雜類型對比調用.NET平臺發佈的WCF服務和WebService服務之間的區別。
0 寫在前面
以前都是在.NET平臺上conding,使用.NET平臺發佈服務,然後再在.NET的客戶端進行調用,非常的方便,最簡單的方式就是添加web服務引用,通過添加web服務引用實現像本地調用那樣調用web服務,當然我們也可以採用http-post、http-get和基於soap協議的方式去調用服務。
最近在弄andriod的程式,需要調用web伺服器上的數據,服務採用C#寫的,並部署在iis伺服器上。我們可以像.NET那樣調用服務那,利用andriod庫自帶的HttpPost和HttpGet類來調用Web服務。但是wcf服務發佈的一些沒有添加WebGet或者WebInvoke特性的服務,都只提供基於Soap協議的服務調用方式。雖然soap協議也是基於Http協議,也可以使用HttpPost類來進行調用,但拼湊soap結構體是比較麻煩,好在Ksoap2包提供了調用web服務的方法,而且還比較好的相容了.NET平臺發佈的服務。因此本文總結在Andriod中如何使用Ksoap2來調用.NET平臺的服務,通過傳遞基類型和複雜類型對比調用.NET服務發佈的WCF服務和WebService服務之間的區別。本文的末尾提供Ksoap2包的下載。
1 WCF服務
我們在服務中提供兩個方法,一個計算整數加法,另一個接受People對象並且返回People信息(string)。
1.1 People的數據契約
[DataContract]
public class People
{
[DataMember]
public int Age;
[DataMember]
public string Name;
}
2.2 WCF服務契約
[ServiceContract(Name = "JuameService", Namespace = "http://www.juame.edu")]
public interface ITest
{
[OperationContract]
int Add(int op1, int op2);
[OperationContract]
string PostPeopleInfo(People people);
}
上面的服務契約設置了Namespace特性,該特性重要。在後面的wb服務調用中需要用到。
2.3 WCF服務實現
public class TestService : ITest
{
public int Add(int op1, int op2)
{
return op1 + op2;
}
public string PostPeopleInfo(People people)
{
return "姓名:"+people.Name+"/"+"年齡"+people.Age;
}
}
我們需要把服務部署到IIS中去,因此需要添加一個svc文件,把服務實現的代碼寫在svc文件中,發佈後,服務調用的地址就是svc文件的地址。
2.4 服務配置
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- 為避免泄漏元數據信息,請在部署前將以下值設置為 false -->
<serviceMetadata httpGetEnabled="true"/>
<!-- 要接收故障異常詳細信息以進行調試,請將以下值設置為 true。在部署前設置為 false 以避免泄漏異常信息 -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Juame.Service.TestService">
<endpoint address=""
binding="basicHttpBinding"
contract ="Juame.Service.ITest">
</endpoint>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
若要在調試過程中瀏覽 Web 應用程式根目錄,請將下麵的值設置為 True。
在部署之前將該值設置為 False 可避免泄露 Web 應用程式文件夾信息。
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
主要是配置好Service節點和serviceBehaviors就行,服務採用BasicHttpBinding類型。在這裡多提一點。BasicHttpBinding是針對於Soap Web Service協議,而webHttpBinding支持web service協議,因此在wcf服務上加上WebGet或WebInvoke特性的必須要使用webHttpBinding類型。
在iis中發佈web服務非常簡單和部署asp.net網站一樣,服務發佈成功之後,能訪問到svc的地址。
我們提供的服務,一個是傳遞基類型(string,int,float等),另外一個是傳遞對象(複雜類型)。
2 Android調用WCF服務
2.1 android佈局
界面佈局非常簡單,兩個Button,一個TextView,按鈕分別用來調用兩個服務,而TextView用來顯示服務調用的結果。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.soapprousage.MainActivity" >
<Button android:id="@+id/btn_jlx"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="基類型調用"/>
<Button android:id="@+id/btn_obj"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="對象調用"/>
<TextView android:id="@+id/lbl_result"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAlignment="viewStart"/>"
</LinearLayout>
2.2 利用Ksoap2調用wcf服務
首先把下載下來的Jar格式的Ksoap包複製到libs(自己創建)文件夾下。
聲明服務調用需要的地址和方法
private String nameSpace="http://www.juame.edu";//和wcf服務契約特性的Namespace是一樣的
private String url="http://172.21.212.54:8888/TestService.svc";//svc服務地址
private String soapAction="http://www.juame.edu/JuameService/Add";//操作地址
private String methodName="Add";//方法名稱
上面聲明的服務地址、命名空間、操作地址和方法名稱都可以從服務的wsdl文檔中查看,
下麵利用ksoap2對服務進行調用的代碼如下。
protected SoapObject getSoapResult(int op1,int op2){
SoapObject outObject=new SoapObject(nameSpace,methodName);
//添加輸出參數
outObject.addProperty("op1", op1);
outObject.addProperty("op2",op2);
SoapSerializationEnvelope serializationEnvelope=new SoapSerializationEnvelope(SoapEnvelope.VER11);//設置soap版本
serializationEnvelope.bodyOut=outObject;
serializationEnvelope.dotNet=true;//調用.NET的服務
HttpTransportSE transportSE=new HttpTransportSE(url);
transportSE.debug=true;//採用調試
try{
transportSE.call(soapAction, serializationEnvelope);//調用服務
SoapObject result=(SoapObject)serializationEnvelope.bodyIn;//獲取調用結果
Log.v("happy1", "服務調用成功");
//把結果封送到消息中去,讓ui線程顯示
Bundle bundle=new Bundle();
bundle.putString("result", result.getProperty(0).toString());//獲取結果中的值
Message message=new Message();
message.setData(bundle);
message.what=12;
hander.sendMessage(message);
return result;
}catch(IOException ex){
Log.v("sad", "IO異常");
ex.printStackTrace();
}catch(XmlPullParserException ex){
Log.v("sad", "xml解析異常");
ex.printStackTrace();
}catch(Exception ex){
Log.v("sad", "服務調用異常異常");
}
return null;
}
按鈕事件代碼,採用多線程。在android3.0後,有關網路資源的調用代碼都不能直接在主UI線程中調用,否則會出現android.os.NetworkOnMainThreadException異常。關於android中的多線程機制有時間再進行總結。
//綁定按鈕事件
btnJlx.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Thread thread=new Thread(getSoapRequest);
thread.start();
}
});
//線程
Runnable getSoapRequest=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
getSoapResult(10, 20);
}
};
//消息處理
Handler hander = new Handler() {
@Override
public void handleMessage(Message msg) {
if(msg.what=12){
lblResult.append(msg.getData().getString("result")+"\r\n");
}
}
};
到目前為止,我們已經調用了wcf服務第一個服務,就說明傳遞基類型是沒有問題。但是很遺憾的是,對於傳遞複雜類型和數組集合參數進行調用,在服務那邊總是提示無法對傳遞進來的數據進行反序列化的錯誤(希望高手指點)。還好我們可以把所以的服務類型都轉為json數據,通過json數據進行傳遞調用,就可以解決複雜類型傳遞的問題。
對於有強迫症的我來說,不甘心,因為在網上看了許多的教程,利用Ksoap2是可以直接傳遞複雜類型的來調用.NET平臺的服務的。不過網上大部分教程調用的都是傳統的webservice服務(asmx文件),於是我就在wcf服務項目中新建一個傳統的asmx文件,提供的服務與wcf服務一樣的。結果發現,果然能夠利用ksoap2傳遞一個複雜類型來調用服務。下一節總結利用ksoap2傳遞複雜對象來調用傳統的webservice服務。
3 傳統的WebService服務
為了和wcf服務進行對比,webservice提供的服務和wcf一致,代碼如下:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允許使用 ASP.NET AJAX 從腳本中調用此 Web 服務,請取消註釋以下行。
// [System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService
{
[WebMethod]
public int Add(int op1, int op2)
{
return op1 + op2;
}
[WebMethod]
public string PostPeopleInfo(People people)
{
return "姓名:" + people.Name + "/" + "年齡:" + people.Age;
}
}
其中WebService特性中的Namespace屬性和wcf的Namespace的作用一樣的。
同樣的也在iis中進行發佈。發佈成功之後,能夠訪問到asmx文件。
4 Android調用WebService服務
不管是調用WCF的服務還是WebService的服務,傳遞基類型去調用,代碼都是一樣的,且能夠正確的調用。下麵利用複雜的People類型來調用WebService的服務。
我們需要傳遞複雜類型,首先我們要在android中建立一個複雜類型,並且複雜類型包含欄位名稱和個數一定要與服務上的複雜類型保持一致,對於服務的複雜類型具有哪些欄位,我們可以通過查看服務調用的示例得知。如下圖所示。
根據上面複雜類型的欄位說明,我們在android中建立複雜類型(類型名可以隨意),包含兩個欄位且欄位名稱必須是Age和Name,數據類型也要一致,上面的這個people代表該複雜類型形參名為people(服務調用的時候必須要保持一樣)。需要註意的是,這個複雜類型必須要繼承KvmSerializable,這樣ksoap2進行服務調用的時候,能夠把people對象序列化為服務端能夠接受的格式。代碼如下:
public class People implements KvmSerializable {
public int Age;
public String Name;
@Override
public Object getProperty(int arg0) {
switch (arg0){
case 0:
return Age;
case 1:
return Name;
default:
return null;
}
}
@Override
public int getPropertyCount() {
return 2;
}
@Override
public void getPropertyInfo(int arg0, Hashtable arg1, PropertyInfo arg2) {
switch (arg0){
case 0:{
arg2.type = PropertyInfo.INTEGER_CLASS;
arg2.name = "Age";
break;
}
case 1:{
arg2.type = PropertyInfo.STRING_CLASS;
arg2.name = "Name";
break;
}
}
}
@Override
public void setProperty(int arg0, Object arg1) {
switch (arg0){
case 0:{
Age = Integer.parseInt(arg1.toString()) ;
break;
}
case 1:{
Name = arg1.toString();
break;
}
}
}
}
下麵是傳遞複雜對象調用web服務,其中服務地址、操作地址、方法名以及命名空間和前面一樣,只需要在服務說明wsdl文檔中找operation name節點和operation soapAction節點的值即可,其他地方也類似,只是在封裝soapobject的時候有一些區別,代碼如下:
//地址聲明
private String nameSpace="http://tempuri.org/";
private String url="http://172.21.212.54:8888/WebService.asmx";
private String soapAction="http://tempuri.org/PostPeopleInfo";
private String methodName="PostPeopleInfo";
//服務調用
protected SoapObject getSoapResult() {
SoapObject outObject = new SoapObject(nameSpace, methodName);
People people = new People();
// 設置欄位值
people.setProperty(0, 23);
people.setProperty(1, "Juame");
// 設置SoapObject對象
outObject.addProperty("people", people);
//也可以這樣設置SoapObject
/*PropertyInfo peoInfo = new PropertyInfo();
peoInfo.setName("people");
peoInfo.setValue(people);
peoInfo.setType(People.class);
outObject.addProperty(peoInfo);*/
SoapSerializationEnvelope serializationEnvelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);//設置soap版本
// 這一步添加映射非常關鍵
// 第一個參數為命名空間,第二參數為伺服器中複雜類型的名稱,第三參數是安卓的複雜類型
serializationEnvelope.addMapping(nameSpace, "People", People.class);
serializationEnvelope.bodyOut = outObject;
serializationEnvelope.dotNet = true;// 調用.NET的服務
HttpTransportSE transportSE = new HttpTransportSE(url);
transportSE.debug = true;// 採用調試
try {
transportSE.call(soapAction, serializationEnvelope);// 調用服務
Log.v("happy1", "服務調用成功");
SoapObject result = (SoapObject) serializationEnvelope.bodyIn;
//把結果封送到消息中去,讓ui線程顯示
Bundle bundle = new Bundle();
bundle.putString("result",result.getProperty(0).toString());
Message message = new Message();
message.setData(bundle);
message.what = 11;
hander.sendMessage(message);
return result;
} catch (IOException ex) {
Log.v("sad", "IO異常");
ex.printStackTrace();
} catch (XmlPullParserException ex) {
Log.v("sad", "xml解析異常");
ex.printStackTrace();
} catch (Exception ex) {
Log.v("sad", "服務調用異常異常");
}
return null;
}
//按鈕事件
btnObj.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Thread thread = new Thread(getSoapRequest);
thread.start();
}
});
//線程
Runnable getSoapRequest=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
getSoapResult();
}
};
//消息處理
Handler hander = new Handler() {
@Override
public void handleMessage(Message msg) {
if(msg.what=11){
lblResult.append(msg.getData().getString("result")+"\r\n");
}
}
};
上面的代碼就能夠傳遞複雜類型去調用WebService的服務,返回結果如下:
姓名:Juame/年齡:23
5 簡述Wcf與WebServic的區別
WebService是一個行業標準,也是Web Service的規範,既不是框架,也不是技術,它使用xml擴展標記語言來表示數據,這正是WebService能夠跨語言和平臺的關鍵,而微軟的Web服務實現稱為ASP.NET Web Service.它使用Soap簡單對象訪問協議來實現分散式環境里應用程式之間的數據交互。
WCF 是一個分散式應用的開發框架,屬於特定的技術,或者平臺。既不是標準也不是規範。在一定程度上就是WebService,不得不說WCF確實非常方便,提供非常多且好用的特性,可以用來創建各種服務,而且自定義性也高,以後項目的服務搭建都會基於WCF來實現。
6 小結
本文總結瞭如何使用android調用web服務。在傳遞複雜類型調用服務的時候糾結的了半天,最後實現了傳遞複雜類型調用WebService服務,但沒有實現對WCF服務的調用,而傳遞基類型調用服務,兩者都可以。在第5小節中還簡述了wcf和webservice之間的區別,其實在項目大都是採用wcf框架來發佈自己的服務。下麵會繼續總結如何用javascript來調用wcf發佈的服務。
另Ksaop2下載鏈接:http://download.csdn.net/download/mingge38/9666650