Retrofit2+Okhttp3+Rxjava通過SOAP協議請求WebService
前言
剛入職新公司,負責其Android App的開發,他們的接口訪問是通過SOAP協議實現的(基于xml數據格式的數據交換規范),由于之前也沒有接觸過這種東西,對此也是一臉懵逼,好在公司之前通過AnsysTask封裝了SOAP的工具類,那就先做個一不明真相的吃瓜群眾,直接拿來使用吧。
先用著再說
作為一個有上進心的程序員,你會甘心停止不前么?管你會不會,反正我是不會哈哈哈.....在使用的過程中發現AnsysTask封裝的SOAP還是存在一定的 缺陷(具體是什么就不闡述了QAQ),由于項目的功能還在擴充,需求也在不斷的改,我就想對 這種請求進行優化,可是那個封裝代碼寫得那叫一個亂,關鍵還沒注釋(哎呀。。。腦袋瓜子疼,你知道這是有多么痛苦!),受盡折磨,果斷放棄不干。前段時間公司要重新開發一個App,我打算放棄之前的架構,重新開始,包括網絡請求框架,可是選擇什么比較好呢?打開技術論壇你就會發現什么Retrofit啊,okhttp,Rxjava,MVP的架構模式之間的各種整合的Demo,發展可謂是風生水起,這么好的東西我也不會錯過嘿嘿,當然每個工具都有它流行的意義,經過一段時間對它們的了解,最終決定使用etrofit2+Okhttp3+Rxjava封裝SOAP來請求WebService,當然這其中也有過坎坷,畢竟網上對于這種問題的提問和回答少之又少。
輾轉很久,最終谷歌給我了答案,所以我決定把我的經驗進行分享,希望對有需求的小伙伴有所幫助。接下來我會對具體實現進行詳細分析。
分析
什么是SOAP?
它是基于xml規范的數據交換協議,WebServices就是基于 XML 和 HTTP 的,簡單點講使用soap對WebServices進行訪問請求就是普通的HTTP,只不過參數類型是以XML的格式進行傳遞和交流,明白了這點,我們就可以對我們的請求參數進行封裝,封裝成為WebServices能夠識別的數據格式進行普通的Http請求就可以,管它后臺服務器是java寫的還是.NET寫的,這時就與我們無關了,但是每個公司定義的數據類型各不相同,在拼接封裝時候需要對數據進行分析和調試。
首先我們需要使用HTTP請求的調試工具,這類工具很多,我這里使用REST Client,它是一個谷歌瀏覽器的插件,安裝到谷歌瀏覽器里很方便使用,在調試之前還是先看一下請求和返回的數據格式規范(這是我們公司的一個接口數據規范)。
這個是數據請求的格式
每次請求服務器都會對你的請求進行驗證,里面有“UserName”和“PassWord”(可能是為了安全性考慮需要驗證吧),然后就是請求參數,該參數是一個String類型的字符串,如果是多個參數的話需要拼接成一個字符串進行處理。該字符串也是有嚴格的規范,一不小心就會掉入坑中,別問我是怎么知道的O.o(曾經爬了一天才脫坑)。
這個是返回數據的格式
真正的返回有效數據就是里面的String。
知道了數據的請求和返回的數據格式,接著我們就可以進行調試了
http模擬請求
參數類型的拼接需要遵循這樣的規范,這里需要對“<”和“>”進行 轉義字符處理,否則的話就直接當作xml的節點了,這樣的話是不符合服務器的參數規法就會返回錯誤數據,可以測試一下,效果如下:
參數沒有進行處理
這個時候就會返回失敗:
參數錯誤,返回失敗
請求參數規范,需要轉義字符
請求成功返回的json數據
由以上的調試我們可以發現,在進行soap請求只是參數和頭部信息不同而已,事實就是這樣,我們把它當作普通http請求,在參數上動點手腳就可以了。從數據的請求格式可以看出每一個請求都不許要添加Content-Type: text/xml; charset=utf-8,這是用來說明我們上傳的參數類型是xml格式的,SOAPAction: "http://tempuri.org/ADInquiry"這里調用的每個接口都不一樣,其實不一樣的就是“ADInquiry”這個參數會變化。
返回的數據類型也是xml形式的,我們可以把返回數據看作字符串來進行處理。對數據進行適當截取,里面的json才是我們真正想要的。
分析到這里已經差不多了,廢話不多說,直接擼代碼!
具體實現
開始之前先放出鎖需要的依賴:
Rxjava依賴:
compile'io.reactivex:rxandroid:1.2.1'
compile'io.reactivex:rxjava:1.1.6'
</code></pre>
OkHttp依賴:
compile'com.squareup.okhttp3:okhttp:3.4.1'
compile'com.squareup.okhttp3:logging-interceptor:3.4.1'
compile'com.squareup.okhttp3:okhttp-urlconnection:3.4.1'</code></pre>
Retrofit依賴:
compile'com.squareup.retrofit2:retrofit:2.0.1'
compile'com.squareup.retrofit2:converter-scalars:2.1.0'
compile'com.squareup.retrofit2:converter-gson:2.1.0'
compile('com.squareup.retrofit2:converter-simplexml:2.1.0') {
excludegroup:'xpp3',module:'xpp3'
excludegroup:'stax',module:'stax-api'
excludegroup:'stax',module:'stax'
}</code></pre>
新建Interface請求接口:

需要通過注解方法添加頭部信息
需要對請求的參數進行拼接處理,這里我們選擇字符串的拼接,可能有的人會問,simplexml支持實體類的拼接啊,通過注解就可以輕松實現xml數據的轉化,為什么不用而選擇字符串呢?答案就是封裝!封裝!封裝!(重要話說三遍),因為我們的請求數據格式字段比較多,每個接口請求的話大約需要建立五個實體并且進行賦值,記得,是每個接口,這是多么繁重的工作,果斷拋棄,但是呢,我還是打算在文章最后對這種使用方法進行簡單說明一下,誰讓Retrofit如此強大呢!

對請求參數進行拼接處理
封裝RequestManager網絡請求工具類,采用單例模式:
public final static intCONNECT_TIMEOUT=10;
public final static intREAD_TIMEOUT=20;
public final static intWRITE_TIMEOUT=10;
public RetrofitmRetrofit;
protected Map params;
private staticRequestManager manager;//管理者實例
publicOkHttpClient mClient;//OkHttpClient實例</code></pre>
在構造方法中進行數據初始化:
1:對OkHttp進行緩存,請求超時,重連機制進行設置;
2:對Retrofit進行設置并與OkHttp進行關聯;
private RequestManager() {
Strategy strategy =newAnnotationStrategy();
Serializer serializer =newPersister(strategy);
HttpLoggingInterceptor interceptor =newHttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
File httpCacheDirectory =newFile(App.getInstance().getCacheDir(),"retrofit");
intcacheSize =3210241024;
Cache cache =newCache(httpCacheDirectory, cacheSize);
OkHttpClient.Builder builder =newOkHttpClient.Builder();
builder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
builder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
builder.retryOnConnectionFailure(true);
builder.addInterceptor(interceptor);
builder.addNetworkInterceptor(getNetWorkInterceptor());
builder.addInterceptor(getInterceptor());
builder.cache(cache);
mClient= builder.build();
mRetrofit=newRetrofit.Builder()
.baseUrl(Constans.WEBSERVICE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(SimpleXmlConverterFactory.create(serializer))
.client(mClient)
.build();
}</code></pre>
新建execute()方法,主要請求和處理網絡數據,服務器返回的 數據是xml形式,我們需要對數據進行解析,把結果看作String類型的字符串,然后進行分割就可以得到json數據:

execute
鐺鐺.....Rxjava上場了,doRequest方法內部就是通過Rxjava,把異步的結果返回到主線程,輕松實現主線程與非主線程之間的相互切換(是不是突然也感覺到Rxjava真是太好用了),否則的話,你第一時間想到的可能就是Handler。

doRequest
對OkHttp攔截器進行設置,有網絡時候請求數據,沒有網絡從緩存讀取 數據,比較遺憾的是還沒有找到更好的方法去設置SOAP的緩存(貌似SOAP不支持緩存),普通的請求是完全支持的:

攔截器設置
定義一個RequestCallBack接口,用于把請求結果進行回調處理:
public interfaceRequestCallBack {
voidonSueecss(String msg);
voidonError(String msg);
voidonStart();
voidonFinish();
}
</code></pre>
調用方式,由于項目是基于MVP模式來寫的,接口層次比較多,這里就簡單的說明一下調用方式:
Map map=newHashMap<>();
map.put("DoctorMobile",name);
map.put("Password",psd);
map.put("PhoneType","0");
map.put("ClientID","");
map.put("DeviceToken","");
String result= Node.getResult("MSDoctorLogin",map);
finalServiceStore service=manager.create(ServiceStore.class);
Call call=service.login(result);</code></pre>
Map集合里面存放的就是參數的值,然后通過Node的getResult()方法進行數據拼接,返回拼接后帶參數的字符串。對于json字符串的解析有很多方法和工具類,Retrofit也可以對返回json進行轉化,這里我就使用最原始的解析方式。

execute

execute
以上就是Retrofit+Soap對webservice進行訪問請求具體實現,該實現是通過String字符串的拼接,傳輸過程中轉化為xml數據格式來實現的,接下來順便提一下另一種方式,通過SimpleXml注解實體類的方式實現轉化。對SimpleXml不太了解的話可以在網上百度一下,使用很簡單,
廢話不多說,我們用代碼說話!
通過對請求數據格式的分析可以清晰的看到,里面包含五個節點,分別為:soap:Envelope,soap:Header,Identify,soap:Body,ADInquiry,好的,就是這樣,毫無疑問需要建立五個實體類。
Envelope實體類:

根節點Envelope
Header實體類:

Header實體類
Identify實體類:

Identify實體類
Body實體類:

Body實體類
ADInquiry實體類:

ADInquiry實體類
請求接口:

請求接口
注意這個時候getInfo()方法里面的參數就是一個實體。
對實體進行初始化和賦值:

初始化和賦值
進行請求:

進行請求
OK,大功告成,可以進行數據請求了,請求的結果我就通過log輸出,親自測試成功訪問。
來自:http://www.jianshu.com/p/b865c855a1e8