Android調用Web服務


現在大部分應用程序都把業務邏輯處理,數據調用等功能封裝成了服務的形式,應用程序只需要調用這些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的地址。

2016_10_6798d44e-3b10-4b21-9b40-66d024199b2f

我們提供的服務,一個是傳遞基類型(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文檔中查看,

2016_10_bab1b9f2-cb82-4092-a3e3-e3fc133b4186

下面利用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中建立一個復雜類型,並且復雜類型包含字段名稱和個數一定要與服務上的復雜類型保持一致,對於服務的復雜類型具有哪些字段,我們可以通過查看服務調用的示例得知。如下圖所示。

2016_10_485cf694-7bf4-409c-9074-a849cc9cb329

根據上面復雜類型的字段說明,我們在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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM