背景
沒啥背景,實在是受夠了ksoap2這個jar包,而公司服務端是基于c#語言的.net開發(fā),不懂他們的技術,而他們好像只能通過WebService與我們Android端進行數據交互(如果有前輩知道別的技術,望指點!謝謝~).
Retrofit作為當前最火的網絡請求框架.如果不去學,永遠不會用.因此,我想把這個框架引入到公司項目里邊來,把ksoap2替換掉,用Retrofit來訪問WebService
既然有了想法,就要去做.網上關于Retrofit的講解一大堆,奈何關于使用Retrofit去訪問WebService的文章少之又少,并且各位前輩的經驗都是基于自己公司的業(yè)務情況總結出來的,和我現(xiàn)在的情況多少有些出入,因此,我把各位前輩的經驗綜合起來,寫了這篇文章,一來,總結下經驗;二來,希望以后有朋友再遇到這種問題的時候,能少走些彎路.
首先將前輩的鏈接奉上:
Retrofit2+Okhttp3+Rxjava通過SOAP協(xié)議請求WebService
在WebService中使用Retrofit+RxJava
轉載----使用 Retrofit 操作 SOAP Web Service
也正是有了各位前輩總結的經驗,才有了我今天這篇文章,向各位前輩致敬!
第一次寫文章,如有不妥之處,希望各位前輩指出!
工具準備
- FireFox(火狐瀏覽器)
- RESTClient(火狐瀏覽器調試插件)
這里,我使用的是火狐瀏覽器+RESTClient去調試http請求.
因為每個公司定義的格式可能會不一樣.我們在封裝以及分析數據的時候需要與之對應.所以我們去調試分析http請求.弄清每次請求及響應的格式.
關于soap,WebService以及http,各位前輩已經分析的很透徹了,這里我就不多說了,如果想了解的話,可以去看下前輩總結的文章.下面,咱們正式開始.
開工
RESTClient界面
在火狐瀏覽器中安裝好RESTClient插件后,將其打開,界面應該會和圖1類似,是空的,沒有任何數據.為了方便分析,我又截取了圖2,明顯是一次成功請求后的界面,我先用圖2分析下整個界面,然后再告訴你,這些數據都是怎么填上去的.

在圖2中,我把一次請求分為了兩個部分,分別用綠線和藍線框了起來.其中,綠線內是本次http請求發(fā)送的數據,也就是我們作為Android端需要封裝的數據,藍線內,就是本次請求服務端返回給我們的數據,也就是我們需要解析的數據.
我們先來分析下我們需要發(fā)送的數據,如圖所示,我用紅線標出了4個位置,它們分別表示什么意思呢?
- Method: 表示這次請求的請求方式,一般常用的有get和post,這里當然選post(因為WebService就是post請求的一種)
- URL: 表示這次請求的地址
- Headers: 請求頭
-
Body: 請求體
圖2
調試http請求
介紹完了RESTClient的界面,下面就正式開始http調試.
所謂調試,無非就是模擬一次http請求,我們把需要發(fā)送給服務端的數據填到Request(圖2綠線內)中,點擊send按鈕,然后Response(圖2藍線內)中顯示服務端返回給我們的數據.我們就是分析這堆數據而已.那么問題來了,Request中的這些數據,是從哪來的?
我另外打開了一個瀏覽器頁面,在地址欄中輸入想要調試的地址,如圖3所示,為了方便,我讓服務端同事把服務器部署到我的電腦上了,所以看到的地址ip是192.168.191.1,
剛剛在地址欄輸入的就是這次要調試的地址,因此,我把他填入到了RESTClient的URL中.
既然url確定了,那請求頭(Headers)和請求體(Body)又該填什么呢?別急,接著往下看.

這次我要調試的就是圖3中紅線內的接口.名為AssetMaterialInfo,點擊這個接口,打開的界面如圖4所示.圖中有SOAP1.2請求和響應示例,紅線標注的是占位符,在模擬數據的時候需要將其替換為真實數據.
而我們所需要的請求頭(Headers)和請求體(Body)就藏在請求示例中.為了方便分析,我單獨將請求示例截取了圖片,放在下邊,也就是圖5.

圖5中,紅線內這兩行內容,就是請求頭,藍線內的就是請求體.請求體很簡單,我們只需要將藍線中內容復制到RESTClient界面的Body中,然后把占位符替換掉就可以了(如圖2所示),請求頭怎么弄呢?

關于在RESTClient中添加請求頭,我舉一個例子(圖6),大家就都明白了.
在RESTClient界面中,點擊頂部Headers,再點擊CustomHeader,會打開圖7這個界面.

在圖7所示界面,Name欄中填入Content-Type,Value欄中填入text/xml; charset=utf-8,然后點擊Okay,我們就將一個請求頭添加到本次請求中了.
同理,將第二個請求頭也添加進來.我就不再演示了.

這樣,我們就將本次請求需要攜帶的數據都添加進來了.點擊SEND按鈕,就完成了本次請求.
開始寫Demo
拿到了http請求的數據,我們就可以根據數據去寫我們的例子程序了,在我們Android端通過Retrofit使用post請求去訪問剛剛我們調試的WebService接口.
首先,在看下邊代碼之前,你要保證自己已經基本了解了Retrofit框架.關于Retrofit不會介紹太多,因為它不是本篇文章的重點.
導包
compile'com.squareup.retrofit2:retrofit:2.0.1'
compile('com.squareup.retrofit2:converter-simplexml:2.1.0') {
exclude group:'xpp3',module:'xpp3'
exclude group:'stax',module:'stax-api'
exclude group:'stax',module:'stax'
}
請求體實例
對應圖2中Request的Body部分,需要寫三個類來作為請求體.分別對應請求體中的三個節(jié)點
根據由外到內的層級關系,它們分別是(類名 <---> 節(jié)點名):
- RequestEnvelope <---> soap:Envelope
- RequestBody <---> soap:Body
- RequestModel <---> AssetMaterialInfo
下面,我詳細介紹下這三個類.
@Root(name = "soap:Envelope")
@NamespaceList({
@Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
@Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),
@Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")
})
public class RequestEnvelope {
@Element(name = "soap:Body", required = false)
public RequestBody body;
}
RequestEnvelope類中,Root注解用來指定節(jié)點名稱,NamespaceList用來指定多個命名空間,Element用來指定子節(jié)點名稱.
@Root(name = "soap:Body", strict = false)
public class RequestBody {
@Element(name = "AssetMaterialInfo", required = false)
public RequestModel AssetMaterialInfo;
}
同理,RequestBody類中,Root注解用來指定節(jié)點名稱,Element注解用來指定子節(jié)點名稱,因為當前節(jié)點沒有命名空間,因此不需要NamespaceList注解
@Root(name = "AssetMaterialInfo", strict = false)
@Namespace(reference = "http://tempuri.org/")
public class RequestModel {
@Element(name = "date", required = false)
public String date;
@Element(name = "page", required = false)
public int page;
}
RequestModel類中,Root注解用來指定節(jié)點名稱,因為當前節(jié)點只有一個命名空間,因此使用Namespace注解而不是NamespaceList.Element注解指定子節(jié)點名稱,因為當前節(jié)點有兩個子節(jié)點,因此這個類有兩個參數,且都被Element注解修飾.
到這里,關于請求體的實體類,就創(chuàng)建好了.下面我們準備響應體的實體類.
響應體實例
跟請求體類似,我們也是需要根據服務器響應的xml文件格式來創(chuàng)建實體類.
通過分析xml格式,我們也需要創(chuàng)建三個實體類來分別對應三個節(jié)點.它們的對應關系如下(類名 <---> 節(jié)點名):
- AssetResponseEnvelope <---> soap:Envelope
- AssetResponseBody <---> Body
- AssetResponseModel <---> AssetMaterialInfoResponse
為了與系統(tǒng)類名區(qū)分開,我為這三個類名添加了Asset前綴.
@Root(name = "soap:Envelope")
@NamespaceList({
@Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance/", prefix = "xsi"),
@Namespace(reference = "http://www.w3.org/2001/XMLSchema/", prefix = "xsd"),
@Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")
})
public class AssetResponseEnvelope {
@Element(name = "Body", required = false)
public AssetResponseBody responseBody;
}
@Root(name = "Body", strict = false)
public class AssetResponseBody {
@Element(name = "AssetMaterialInfoResponse", required = false)
public AssetResponseModel responseModel;
}
@Root(name = "AssetMaterialInfoResponse")
public class AssetResponseModel {
@Attribute(name = "xmlns", empty = "http://tempuri.org/", required = false)
public String nameSpace;
@Element(name = "AssetMaterialInfoResult")
public String result;
}
因為與請求體類似,這里我也不用對這三個類做過多介紹.不過,值得一提的是,在AssetResponseModel類中,我沒有再用Namespace注解去指定命名空間,而是添加了一個成員變量,用Attribute注解將其指定.需要注意的是,再用Attribute注解指定的時候,name,empty,required三個屬性,缺一不可.
下面奉上SimpleXml的地址,上面有關于xml與實體類之間綁定的詳細介紹:
SimpleXml
創(chuàng)建Interface
三個請求類和三個響應類創(chuàng)建好了,下一步就是創(chuàng)建請求接口Interface.(不明白為啥要這樣做的,可以去復習下Retrofit,哦不對,是預習~)
直接上代碼:
public interface ApiStore {
@Headers({
"Content-Type: text/xml; charset=utf-8",
"SOAPAction: http://tempuri.org/AssetMaterialInfo"
})
@POST("GetService.asmx")
Call<AssetResponseEnvelope> getAssetInfo(@Body RequestEnvelope requestEnvelope);
}
在請求接口中,我們定義了一個函數,將其請求方式制定為post請求,并且為其添加了請求頭.這個函數接收一個RequestEnvelope參數.
使用Retrofit請求
所有初始化工作都做完之后,使用Retrofit去請求WebService這塊還是蠻簡單的.
不得不說,Retrofit確實很強大,整個請求流程下來,結構清晰明了,一點都不拖泥帶水.如果再結合上RxJava,豈不是更爽?
/**
* 去服務端請求數據
*/
private void request() {
String url = "http://192.168.191.1:2000/";
// 初始化Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(SimpleXmlConverterFactory.create()) // 返回數據為xml,因此要加入xml解析
.build();
ApiStore apiStore = retrofit.create(ApiStore.class);
// 初始化請求體
RequestModel requestModel = new RequestModel("2012-01-01", 0);
RequestBody requestBody = new RequestBody(requestModel);
RequestEnvelope requestEnvelope = new RequestEnvelope(requestBody);
// 開始請求
Call<AssetResponseEnvelope> call = apiStore.getAssetInfo(requestEnvelope);
call.enqueue(new Callback<AssetResponseEnvelope>() {
@Override
public void onResponse(Call<AssetResponseEnvelope> call, Response<AssetResponseEnvelope> response) {
// 處理響應體
AssetResponseEnvelope responseEnvelope = response.body();
if (responseEnvelope == null) {
Log.d(TAG, "onResponse: responseEnvelope == null");
return;
}
AssetResponseBody responseBody = responseEnvelope.responseBody;
if (responseBody == null) {
Log.d(TAG, "onResponse: responseBody == null");
return;
}
AssetResponseModel responseModel = responseBody.responseModel;
if (responseModel == null) {
Log.d(TAG, "onResponse: responseModel == null");
return;
}
String result = responseModel.result;
Log.d(TAG, "onResponse: result : " + result);
// showResult(result);
}
@Override
public void onFailure(Call<AssetResponseEnvelope> call, Throwable t) {
}
});
}
總結
到此為止,一個基于Retrofit使用post請求訪問WebService的小Demo就算寫完了.我已經將代碼提交到了GitHub,感興趣的同學可以去看一下,很簡單的小程序.
項目地址
有不明白的同學,歡迎向我提出問題,我們共同學習.
第一次寫文章,還望各位前輩多多批評指正,不勝感激!
