本文主要介紹android用戶端如何使用webservice。第一篇介紹ksoap2,第二篇介紹rest。
android基礎知識10:webservice 01:KSOAP2
android基礎知識10:webservice 02:REST
1、REST執行個體
最近項目中採用Apache CXF 的REST 方式發布WebService實現,Android手機後台服務的開發,以下以簡單是執行個體實現。
在項目中採用Android+REST WebService服務方式開發的手機平台很少採用 soap協議這種方式,主要soap協議解析問題,增加了代碼量。 採用RESTFull 方式開發WebService的好處,相對SOAP協議的WebService來說,比較簡單。同時簡化了在手機解析工作,減輕了手機端的壓力,提高了手機響應的效率。
手機後台服務:
package com.easyway.rest.ws;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;/** * 服務端發布一個簡單的WebService服務 * 在手機端接受服務端發送的資訊. * 使用 Apache HttpClient 庫訪問 JAX-RS web 服務。Jersey 是 JAX-RS * 的參考實現,它簡化了 Java 環境下的 RESTful Web 服務的開發。Android * 是一款流行的智能手機,本文將展示如何為 Android 建立一個 JAX-RS 用戶端。 * 您將建立一個訪問 JAX-RS Web 服務的 Apache HttpClient 庫用戶端。 * JAX-RS必須的jar: * jersey-bundle-1.8.jar,jersey-server-1.10.jar,jsr311-api-1.1.1.jar * asm-3.1.jar * 使用一個 root 資源類建立一個 RESTful Web 服務資源。root 資源類是帶有 @PATH * 注釋的 POJO。它包含至少一個帶注釋的方法,該注釋為 @PATH、@GET、@PUT、@POST * 或 @DELETE。 * * 在伺服器上,按照 web.xml 的指定,init 參數 com.sun.jersey.config.property.resourceConfigClass * 作為 com.sun.jersey.api.core.PackagesResourceConfig 啟動,而 init 參數 * com.sun.jersey.config.property.packages 作為 com.easyway.rest.ws 啟動。 * 找到 root 資源類 com.easyway.rest.ws.HelloWorldResource。 * * 備忘:如果採用jersey發布JAXRS服務需要配置: * <pre> * <servlet>* <description>JAX-RS</description>* <servlet-name>JAX-RS-Servlet</servlet-name>* <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>* <init-param>* <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>* <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>* </init-param>* <init-param>* <param-name>com.sun.jersey.config.property.packages</param-name>* <param-value>com.easyway.rest.ws</param-value>* </init-param>* <load-on-startup>1</load-on-startup>* </servlet>* <servlet-mapping>* <servlet-name>JAX-RS-Servlet</servlet-name>* <url-pattern>/services/*</url-pattern>* </servlet-mapping> * * </pre> * * @author longgangbai * */@Path("/helloworld")public class HelloWorldResource {/** * 一個簡單的文本資訊 * @return */ @GET @Produces(MediaType.TEXT_PLAIN) public String getClichedMessage() { return "Hello Android"; }}
web.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>JAXRSWebService</display-name><servlet> <description>JAX-RS</description> <servlet-name>JAX-RS-Servlet</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name> <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value> </init-param> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.easyway.rest.ws</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JAX-RS-Servlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping></web-app>
手機前台服務:
package com.easyway.rest.ws;import java.io.IOException;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import android.app.Activity;import android.os.Bundle;import android.os.StrictMode;import android.widget.TextView;/** * Android平台主要提供了四種資料存放區方式:Shared Preferences、檔案儲存體、Sqlite儲存和網路儲存。其中: 1)Shared Preferences 一個輕量級的鍵-值儲存機制,專門用於儲存鍵-值對資料,並且僅可以儲存基本的資料類型 (boolean、int、long、float和String);通常使用它來儲存應用程式的配置資訊。 2)檔案儲存體 通過FileInputStream和FileOutputStream對檔案進行操作,在Android中,檔案是一個應用程式私人的, 一個應用程式無法讀寫其它應用程式的檔案。 3)SQLite儲存 SQLite是一款輕型的資料庫,支援標準SQL。它的設計目標是嵌入式的,佔用資源非常的低,在嵌入式裝置中, 只需要幾百K的記憶體就夠了。Android平台也為我們提供了SQLite資料庫。 4)網路儲存 以上3種方式資料均儲存在手機上,而網路儲存的資料是儲存在遠程伺服器上,手機用戶端通過聯結到網路來儲存和擷取資料。 今天要講解的HttpClient正是常用的網路儲存工具之一。記得最早接觸HttpClient是在兩年前,當時要做一個垂直搜尋引擎, 資料自然是來源於互連網,通過一個爬蟲系統不斷從指定網站上爬取感興趣的資料,然後通過Lucene搜尋引擎架構實現海量資料 的快速檢索。而爬蟲系統最開始是想採用開源的爬蟲架構Heritrix來實現,但接觸一段時間後發現Heritrix過於龐大,而且是作 為一個獨立的系統運行,不方便嵌入到現有的系統中,再加上學習成本高,最後還是選擇了“HttpClient + HtmlParser”來實現的 小型爬蟲系統;其中HttpClient可以類比HTTP的POST和GET請求,用於從指定網站擷取網頁資料,而HtmlParser用於解析爬取到 的頁面,過濾HTML標記,取得最終資料。 是不是發現HttpClient還挺強大的?讓我們看看它是什麼來頭。"HttpClient 是 Apache Jakarta Common 下的子項目,可以用來 提供高效的、最新的、功能豐富的支援 HTTP 協議的用戶端編程工具包,並且它支援 HTTP 協議最新的版本和建議"。如果你以前沒 有接觸過HttpClient,那麼你只需要簡單記住兩點就可以了: 1)HttpClient是一個HTTP協議開發包; 2)HttpClient不是Android的專利。 HttpClient的功能介紹: 1)實現了HTTP請求的所有方法(如GET、POST、PUT、HEAD 等); 2)支援自動轉向; 3)支援 HTTPS 協議; 4)支援Proxy 伺服器等 HttpClient的基本使用(以POST請求為例): 1)建立HttpClient執行個體(類似於瀏覽器用戶端); HttpClient client = new DefaultHttpClient(); 2)建立HttpPost請求,需要向HttpPost的構造方法傳入所請求的URL; HttpPost post = new HttpPost(requestUrl); 3)發出POST請求(調用HttpClient的execute()方法,execute()的參數為HttpPost執行個體); HttpResponse response = client.execute(post); 4)讀取返回結果; 5)釋放串連; 6)對返回的結果進行處理。 在Android平台上使用HttpClient,並不需要添加額外的jar包,因為Android平台吸收了許多優秀的開源架構,其中就包括HttpClient, 下面就來看一個Android平台使用HttpClient的例子。備忘:是不是發現HttpClient很容易使用呢?其實,上面所講解的只是HttpClient最基本的功能(發起POST請求);我們在瀏覽器用戶端所執行的大多數操作HttpClient都能夠類比,例如:提交表單、查詢資料、上傳下載文檔、頁面跳轉、Session儲存等。比如大家經常玩“搶車位”、“偷菜”,就可以通過HttpClient編程自動實現。 * * * 用戶端通過Apache HttpClient調用JAXRS WebService的服務。 * 為 Android 開發訪問 JAX-RS Web 服務的 Apache HttpClient 用戶端. * * 備忘:在訪問原生JAXRS Web服務的時候不能使用localhost或者127.0.0.1, * 因為android類比機會調用自身的linux核心作業系統,所以可能找不到相關的服務。 * 最好填寫ip地址如下: * "http://192.168.134.1:8080/JAXRSWebService/services/helloworld"; * * @author longgangbai * */public class AndroidJAXRSWebServiceActivity extends Activity { private static final String processURL="http://192.168.134.1:8080/JAXRSWebService/services/helloworld";private TextView txResult; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { ///在Android2.2以後必須添加以下代碼//本應用採用的Android4.0//設定線程的策略 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog() .build()); //設定虛擬機器的策略 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() //.detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); super.onCreate(savedInstanceState); //設定UI布局 setContentView(R.layout.main); //擷取結果顯示文字框 txResult=(TextView)findViewById(R.id.tvresult); //擷取JAXRS WebService的結果資訊 getJAXRSWebService(); } /** * 擷取JAXRS WebService的結果資訊 */ public void getJAXRSWebService(){ try { //建立一個HttpClient對象 HttpClient httpclient = new DefaultHttpClient(); //建立HttpGet對象 HttpGet request=new HttpGet(processURL); //請求資訊類型MIME每種響應類型的輸出(普通文本、html 和 XML)。允許的響應類型應當匹配資源類中產生的 MIME 類型 //資源類產生的 MIME 類型應當匹配一種可接受的 MIME 類型。如果產生的 MIME 類型和可接受的 MIME 類型不 匹配,那麼將 //產生 com.sun.jersey.api.client.UniformInterfaceException。例如,將可接受的 MIME 類型設定為 text/xml,而將 //產生的 MIME 類型設定為 application/xml。將產生 UniformInterfaceException。 request.addHeader("Accept","text/plain"); //擷取響應的結果HttpResponse response =httpclient.execute(request);//擷取HttpEntityHttpEntity entity=response.getEntity();//擷取響應的結果資訊String result =EntityUtils.toString(entity); txResult.setText(result); } catch (ClientProtocolException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}
2、REST的理論
在SOA的基礎技術實現方式中WebService佔據了很重要的地位,通常我們提到WebService第一想法就是SOAP訊息在各種傳輸協議上互動。近幾年REST的思想伴隨著SOA逐漸被大家接受,同時各大網站不斷開放API提供給開發人員,也激起了REST風格WebService的熱潮。
在收到新需求Email之前,我對REST的理解僅僅是通過半懂不懂的看了Fielding的REST博士論文,說實話當時也就是希望瞭解這麼一個新概念,對於其內部的思想只是很膚淺的瞭解了一下。
ASF的最新需求就是可能需要實現REST風格的WebService整合,因此不得不好好的去看看REST的真正思想含義以及當前各大網站的設計方式。後面所要表述的也是我這個初學者的一些看法和觀點,拋磚引玉,希望在我將REST融入到ASF之前能夠獲得更多的反饋和意見。
2.1 SOAP
什麼是SOAP,我想不用多說,google一把滿眼都是。其實SOAP最早是針對RPC的一種解決方案,簡易物件存取通訊協定 (SOAP),很輕量,同時作為應用協議可以基於多種傳輸協議來傳遞訊息(Http,SMTP等)。但是隨著SOAP作為WebService的廣泛應用,不斷地增加附加的內容,使得現在開發人員覺得SOAP很重,使用門檻很高。在SOAP後續的發展過程中,WS-*一系列協議的制定,增加了SOAP的成熟度等級,也給SOAP增加了負擔。
2.2 REST
REST其實並不是什麼協議也不是什麼標準,而是將Http協議的設計初衷作了詮釋,在Http協議被廣泛利用的今天,越來越多的是將其作為傳輸協議,而非原先設計者所考慮的應用協議。SOAP類型的WebService就是最好的例子,SOAP訊息完全就是將Http協議作為訊息承載,以至於對於Http協議中的各種參數(例如編碼,錯誤碼等)都置之不顧。其實,最輕量級的應用協議就是Http協議。Http協議所抽象的get,post,put,delete就好比資料庫中最基本的增刪改查,而互連網上的各種資源就好比資料庫中的記錄(可能這麼比喻不是很好),對於各種資源的操作最後總是能抽象成為這四種基本操作,在定義了定位資源的規則以後,對於資源的操作通過標準的Http協議就可以實現,開發人員也會受益於這種輕量級的協議。
自己理解的將REST的思想歸結以下有如下幾個關鍵點:
1.面向資源的介面設計
所有的介面設計都是針對資源來設計的,也就很類似於我們的物件導向和面向過程的設計區別,只不過現在將網路上的操作實體都作為資源來看待,同時URI的設計也是體現了對於資源的定位設計。後面會提到有一些網站的API設計說是REST設計,其實是RPC-REST的混合體,並非是REST的思想。
2.抽象操作為基礎的CRUD
這點很簡單,Http中的get,put,post,delete分別對應了read,update,create,delete四種操作,如果僅僅是作為對於資源的操作,抽象成為這四種已經足夠了,但是對於現在的一些複雜的商務服務介面設計,可能這樣的抽象未必能夠滿足。其實這也在後面的幾個網站的API設計中暴露了這樣的問題,如果要完全按照REST的思想來設計,那麼適用的環境將會有限制,而非放之四海皆準的。
3.Http是應用協議而非傳輸協議
這點在後面各大網站的API分析中有很明顯的體現,其實有些網站已經走到了SOAP的老路上,說是REST的理念設計,其實是作了一套私人的SOAP協議,因此稱之為REST風格的自訂SOAP協議。
4.無狀態,自包含
這點其實不僅僅是對於REST來說的,作為介面設計都需要能夠做到這點,也是作為可擴充和高效性的最基本的保證,就算是使用SOAP的WebService也是一樣。
2.3 REST vs SOAP
成熟度等級:
SOAP雖然發展到現在已經脫離了初衷,但是對於異構環境服務發布和調用,以及廠商的支援都已經達到了較為成熟的情況。不同平台,開發語言之間通過SOAP來互動的web service都能夠較好的互連(在部分複雜和特殊的參數和返回對象解析上,協議沒有作很細緻的規定,導致還是需要作部分修正)
REST國外很多大網站都發布了自己的開發API,很多都提供了SOAP和REST兩種Web Service,根據調查部分網站的REST風格的使用方式要高於SOAP。但是由於REST只是一種基於Http協議實現資源操作的思想,因此各個網站的REST實現都自有一套,在後面會講訴各個大網站的REST API的風格。也正是因為這種各自實現的情況,在效能和可用性上會大大高於SOAP發布的web service,但統一通用方面遠遠不及SOAP。由於這些大網站的SP往往專註於此網站的API開發,因此通用性要求不高。
ASF上考慮發布REST風格的Web Service,可以參考幾大網站的設計(兄弟公司的方案就是參考了類似於flickr的設計模式),但是由於沒有類似於SOAP的權威性協議作為規範,REST實現的各種協議僅僅只能算是私人協議,當然需要遵循REST的思想,但是這樣細節方面有太多沒有約束的地方。REST日後的發展所走向規範也會直接影響到這部分的設計是否能夠有很好的生命力。
總的來說SOAP在成熟度等級上優於REST。
效率和易用性:
SOAP協議對於訊息體和訊息頭都有定義,同時訊息頭的可擴充性為各種互連網的標準提供了擴充的基礎,WS-*系列就是較為成功的規範。但是也由於SOAP由於各種需求不斷擴充其本身協議的內容,導致在SOAP處理方面的效能有所下降。同時在易用性方面以及學習成本上也有所增加。
REST被人們的重視,其實很大一方面也是因為其高效以及簡潔易用的特性。這種高效一方面源於其面向資源介面設計以及操作抽象簡化了開發人員的不良設計,同時也最大限度的利用了Http最初的應用協議設計理念。同時,在我看來REST還有一個很吸引開發人員的就是能夠很好的融合當前Web2.0的很多前端技術來提高開發效率。例如很多大型網站開放的REST風格的API都會有多種返回形式,除了傳統的xml作為資料承載,還有(JSON,RSS,ATOM)等形式,這對很多網站前端開發人員來說就能夠很好的mashup各種資源資訊。
因此在效率和易用性上來說,REST更勝一籌。
安全性:
這點其實可以放入到成熟度等級中,不過在當前的互連網應用和平台開發設計過程中,安全已經被提到了很高的高度,特別是作為外部介面給第三方調用,安全性可能會高過商務邏輯本身。
SOAP在安全方面是通過使用XML-Security和XML-Signature兩個規範組成了WS-Security來實現安全控制的,當前已經得到了各個廠商的支援,.net ,php ,java 都已經對其有了很好的支援(雖然在一些細節上還是有不相容的問題,但是互連基本上是可以的)。
REST沒有任何規範對於安全方面作說明,同時現在開放REST風格API的網站主要分成兩種,一種是自訂了安全資訊封裝在訊息中(其實這和SOAP沒有什麼區別),另外一種就是靠硬體SSL來保障,但是這隻能夠保證點到點的安全,如果是需要多點傳輸的話SSL就無能為力了。安全這塊其實也是一個很大的問題,今年在BEA峰會上看到有示範採用SAML2實現的網站間SSO,其實是直接採用了XML-Security和XML-Signature,效率看起來也不是很高。未來REST正常化和通用化過程中的安全是否也會採用這兩種規範,是未知的,但是加入的越多,REST失去它高效性的優勢越多。
2.4 應用設計與改造:
我們的系統要麼就是已經有了那些需要被發布出去的服務,要麼就是剛剛設計好的服務,但是開發人員的傳統設計思想讓REST的形式被接受還需要一點時間。同時在資源型資料服務介面設計上來說按照REST的思想來設計相對來說要容易一些,而對於一些複雜的服務介面來說,可能強要去按照REST的風格來設計會有些牽強。這一點其實可以看看各大網站的介面就可以知道,很多網站還要傳入function的名稱作為參數,這就明顯已經違背了REST本身的設計思路。
而SOAP本身就是面向RPC來設計的,開發人員十分容易接受,所以不存在什麼適應的過程。
總的來說,其實還是一個老觀念,適合的才是最好的
技術沒有好壞,只有是不是合適,一種好的技術和思想被誤用了,那麼就會得到反效果。REST和SOAP各自都有自己的優點,同時如果在一些情境下如果去改造REST,其實就會走向SOAP(例如安全)。
REST對於資源型服務介面來說很合適,同時特別適合對於效率要求很高,但是對於安全要求不高的情境。而SOAP的成熟性可以給需要提供給多開發語言的,對於安全性要求較高的介面設計帶來便利。所以我覺得純粹說什麼設計模式將會佔據主導地位沒有什麼意義,關鍵還是看應用情境。
同時很重要一點就是不要扭曲了REST現在很多網站都跟風去開發REST風格的介面,其實都是在學其形,不知其心,最後弄得不倫不類,效能上不去,安全又保證不了,徒有一個看似象摸象樣的皮囊。
參考文獻:
Android+REST WebService服務方式手機開發
SOAPvsREST_WebService