retrofit2調(diào)用webservice請求一共3篇
一:工具
二:通過retrofit2 進行soap請求
三:攔截器,通過retrofit2 攔截器拼接入?yún)⒑瓦^濾出返回值,使soap請求更趨向于http請求
先優(yōu)化下返回值,截取需要的數(shù)據(jù),用了Rx的compose關鍵字:
/**
* 從網(wǎng)絡請求里面的那串截取出需要的json數(shù)據(jù)
* @return
*/
public static Observable.Transformer<ResponseBody, String> gsonResult(final String papapa) {
return new Observable.Transformer<ResponseBody, String>() {
@Override
public Observable<String> call(Observable<ResponseBody> tObservable) {
return tObservable.flatMap(
new Func1<ResponseBody, Observable<String>>() {
@Override
public Observable<String> call(ResponseBody response) {
try {
if (response == null) {
Log.v("test", "--->請求發(fā)生未知錯誤");
return Observable.error(new ApiException("0002", "接口調(diào)用無返回值"));
}
String res = response.string();// 記得要關閉!!! ResponseBody .string()會自動關閉 .string()和toString()完全不一樣!!!
if (res != null && !res.equals("")) {
// 字符轉義<-->轉義完之后就開始截取數(shù)據(jù)
String subStr = res.replace("<", "<").replace(">", ">");
// success date like this <GetSysDateTimeResult>string</GetSysDateTimeResult>
String ostar = "<" + papapa + "Result>";
String oend = "</" + papapa + "Result>";
if (subStr.contains(ostar) && subStr.contains(oend)) {
int startIndex = subStr.indexOf(ostar) + ostar.length();
int endtIndex = subStr.lastIndexOf(oend);
String ores = subStr.substring(startIndex, endtIndex);
return returnString(ores);
}
}
} catch (Exception e) {
Log.v("test", "--->IOException e: " + e.getMessage());
e.printStackTrace();
return Observable.error(new ApiException("0003", e.getMessage()));
}
// return Observable.empty();
return Observable.error(new ApiException("0001","無"+papapa+"接口信息,請檢查調(diào)用的接口是否正確"));
}
}
);
}
};
}
調(diào)用:
/**
* 通過省份獲取城市代碼,截取數(shù)據(jù)
*/
public void getSupportCityBySecond() {
Map map = new HashMap<>();
map.put("byProvinceName", "福建");
String result = ApiNode.getParameter("getSupportCity", map);
AppClient.getInstance().getSupportCity(result)
.compose(ApiSchedulersHelper.gsonResult("getSupportCity")) // -->獲取從數(shù)據(jù)源獲取json
.subscribeOn(Schedulers.io())// 指定 subscribe() 發(fā)生在 IO 線程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回調(diào)發(fā)生在主線程
.subscribe(new RxSubscriber<String>() {
@Override
public void _onNext(String string) {
Log.e("test", "---getSupportCityBySecond _onNext str--->"+string);
// <string>福州 (58847)</string><string>廈門 (59134)</string><string>龍巖 (58927)</string><string>南平 (58834)</string><string>寧德 (58846)</string><string>莆田 (58946)</string><string>泉州 (59137)</string><string>三明 (58828)</string><string>漳州 (59126)</string>
tv_date.setText(string);
}
@Override
public void _onError(String msg) {
Log.d("test", "--->getSupportCityBySecond msg:" + msg);
tv_date.setText(msg);
}
});
}
接下來要換攔截器的車開了
重新擼一個AppSoapClient,初始化retrofit
import com.blankj.utilcode.utils.EncodeUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import linc.ps.net_common.ApiNode;
import linc.ps.net_common.BuildConfig;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
/**
* Retrofit初始化工具,攔截器模式
*/
public class AppSoapClient {
// 超時時間 默認5秒
private static final int DEFAULT_TIMEOUT = 5;
public static Retrofit mRetrofit;
private ApiSoapStores apiSoapStores;
private AppSoapClient() {
if (mRetrofit == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
// 日志信息攔截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//設置 Debug Log 模式
builder.addInterceptor(loggingInterceptor);
//SOAP請求過濾器
HttpRequestInterceptor httpRequestInterceptor = new HttpRequestInterceptor();
builder.addInterceptor(httpRequestInterceptor);
OkHttpClient okHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.API_SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
}
apiSoapStores = mRetrofit.create(ApiSoapStores.class);
}
/**
* SOAP請求過濾器
*/
static class HttpRequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//獲取原Resuqest
Request originalRequest = chain.request();
//解析出namespace
String url = originalRequest.url().toString();
String namespace = url.replace(BuildConfig.API_SERVER_URL + ApiSoapStores.URL_HEAD, "");
RequestBody requestBody = originalRequest.body();
if(requestBody != null) {
//構建新的ResuqestBody
RequestBody newRequestBody = buildRequestBody(namespace, requestBody);
if(newRequestBody != null) {
//構建新的Request
Request.Builder builder = originalRequest.newBuilder();
Request request = builder
.url(url.replace("/" + namespace, ""))
.addHeader("Content-type", ApiSoapStores.CONTENT_TYPE)
.addHeader("SOAPAction", ApiSoapStores.SOAP_ACTION_HEAD + namespace)
.post(newRequestBody)
.build();
//開始進行網(wǎng)絡請求
Response originalResponse = chain.proceed(request);
if(originalResponse != null) {
//獲取原ResponseBody
ResponseBody responseBody = originalResponse.body();
//構建新的ResponseBody
ResponseBody newResponseBody = parseResponseBody(namespace, responseBody);
if (newResponseBody != null) {
Response.Builder responseBuilder = originalResponse.newBuilder();
//返回新的Resonse
return responseBuilder.body(newResponseBody).build();
}
}
return originalResponse;
}
}
return chain.proceed(originalRequest);
}
/**
* 入?yún)?shù)據(jù)處理
* @param namespace
* @param requestBody
* @return
*/
private RequestBody buildRequestBody(String namespace, RequestBody requestBody) {
Map<String, String> map = new HashMap<>();
if (requestBody instanceof FormBody) {
Log.e("test", "--->有入?yún)?);
FormBody formBody = (FormBody) requestBody;
for (int i = 0; i < formBody.size(); i++) {
String name = formBody.encodedName(i);
String value = EncodeUtils.urlDecode(formBody.encodedValue(i)).replace("<", "<").replace(">", ">").replace("%24", "$");
// String value = formBody.encodedValue(i).replace("<", "<").replace(">", ">").replace("%24", "$");//--->轉義字符得封裝成一個類
map.put(name, value);
}
String newBody = ApiNode.getParameter(namespace, map);// 拼接入?yún)⒌姆椒? Log.e("test", "--->有入?yún)?->"+newBody);
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
RequestBody newRequestBody = RequestBody.create(mediaType, newBody);
Log.e("test", "--->有入?yún)?->"+newRequestBody.toString());
return newRequestBody;
}else{// 無參的特殊處理,因為轉換的FormBody會為空
Log.e("test", "--->無入?yún)?);
String newBody = ApiNode.getParameter(namespace, map);
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
RequestBody newRequestBody = RequestBody.create(mediaType, newBody);
Log.e("test", "--->無入?yún)?->"+newRequestBody.toString());
return newRequestBody;
}
}
/**
* 解析Response
* @param namespace
* @param responseBody
* @return
*/
private ResponseBody parseResponseBody(String namespace, ResponseBody responseBody) {
try {
String res = responseBody.string();
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
// 獲取到的數(shù)據(jù)-->截取
if (res != null && !res.equals("")) {
// 字符轉義
String ostar = "<" + namespace + "Result>";
String oend = "</" + namespace + "Result>";
if (res.contains(ostar) && res.contains(oend)) {
int startIndex = res.indexOf(ostar) + ostar.length();
int endIndex = res.lastIndexOf(oend);
String ores = res.substring(startIndex, endIndex);
return ResponseBody.create(mediaType, ores);
}
}else{
return ResponseBody.create(mediaType, res);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//在訪問HttpMethods時創(chuàng)建單例
private static class SingletonHolder{
private static final AppSoapClient INSTANCE = new AppSoapClient();
}
//獲取單例
public static AppSoapClient getInstance(){
return AppSoapClient.SingletonHolder.INSTANCE;
}
public Observable<ResponseBody> getSupportCity(String byProvinceName){
return apiSoapStores.getSupportCity(byProvinceName);
}
}
retrofit的接口類ApiSoapStores
import okhttp3.ResponseBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import rx.Observable;
/**
* Created by Frank on 2016/12/9.
* (請求有用攔截器)
* 無入?yún)⒌囊サ鬇FormUrlEncoded
*/
public interface ApiSoapStores {
/** head基礎參數(shù) **/
String CONTENT_TYPE = "text/xml; charset=utf-8";
String SOAP_ACTION_HEAD = "http://WebXml.com.cn/";
String URL_HEAD = "WeatherWebService.asmx/";
//** 帶參請求 **//*
@FormUrlEncoded
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity(@Field("byProvinceName") String byProvinceName);
/** 無參請求 **/
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity();
}
無入?yún)⒌囊サ鬇FormUrlEncoded
無入?yún)⒌囊サ鬇FormUrlEncoded
無入?yún)⒌囊サ鬇FormUrlEncoded
重要的事情說三遍
調(diào)用:
/**
* 通過省份獲取城市代碼,截取數(shù)據(jù),中文亂碼de解決的方案-->入?yún)①x值之前手動轉換一次
*/
public void getSupportCityByThrid() {
AppSoapClient.getInstance().getSupportCity(et_cityname.getText().toString())
.subscribeOn(Schedulers.io())// 指定 subscribe() 發(fā)生在 IO 線程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回調(diào)發(fā)生在主線程
.subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
Log.e("test", "---getSupportCityByThrid onCompleted--->");
}
@Override
public void onError(Throwable e) {
Log.e("test", "---getSupportCityByThrid onError--->"+e.getMessage());
}
@Override
public void onNext(ResponseBody response) {
Log.e("test", "---getSupportCityByThrid onNext--->");
try {
String res = response.string();
Log.e("test", "---getSupportCityByThrid onNext str--->"+res);
tv_date.setText(res);
} catch (IOException e) {
Log.e("test", "---getSupportCityByThrid onNext str-IOException-->"+e.getMessage());
e.printStackTrace();
}
}
});
}
上面的和原來沒用攔截器的幾點說明一下:
1.接口會比較清晰
原來:
// 通過省份獲取城市代碼,頭文件在第一篇都有講到
@Headers({
"Content-Type:text/xml; charset=utf-8",
"SOAPAction:http://WebXml.com.cn/getSupportCity"
})
// 這里對應 http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName 里面的WeatherWebService
@POST("WeatherWebService.asmx")
Observable<ResponseBody> getSupportCity(@retrofit2.http.Body String s);
現(xiàn)在:
@FormUrlEncoded
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity(@Field("byProvinceName") String byProvinceName);
2.是入?yún)⒏逦?br>
3.返回值不用在每個請求里面特殊處理去截取數(shù)據(jù)
原來:
Map map = new HashMap<>();
map.put("byProvinceName", "福建");
String result = ApiNode.getParameter("getSupportCity", map);
AppClient.getInstance().getSupportCity(result)
.compose(ApiSchedulersHelper.gsonResult("getSupportCity")) // -->獲取從數(shù)據(jù)源獲取json
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new RxSubscriber<String>() {
@Override
public void _onNext(String string) {
tv_date.setText(string);
}
@Override
public void _onError(String msg) {
tv_date.setText(msg);
}
});
現(xiàn)在:
AppSoapClient.getInstance().getSupportCity(et_cityname.getText().toString())
.subscribeOn(Schedulers.io())// 指定 subscribe() 發(fā)生在 IO 線程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回調(diào)發(fā)生在主線程
.subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody response) {
try {
String res = response.string();
tv_date.setText(res);
} catch (IOException e) {
e.printStackTrace();
}
}
});
因為拼接入?yún)?截取返回值的都在攔截器里做了
但是我這樣寫會遇到兩個問題,現(xiàn)在也還沒有很完美的處理方法:
1.入?yún)⒅形膩y碼的處理
攔截器獲取到的入?yún)ⅲ謩釉俎D了一次
轉換的方法是調(diào)用的網(wǎng)上別人寫的
https://github.com/Blankj/AndroidUtilCode/blob/master/README-CN.md
里面的EncodeUtils.urlDecode,但是用起來還是感覺有點奇怪,像請求接口那邊加@Headers,入?yún)⒏某葽Field(value = "byProvinceName", encoded=true) String byProvinceName,這些測試了沒有效果。
2.現(xiàn)在的項目都是Rx去繼承重寫了Subscriber,所以異常情況(webservice接口不存在,或者http的異常處理)都可以捕獲而且去判斷,然后做出相應的提示如果,在攔截器里不知道怎么很好的去實現(xiàn)這個。
以上問題有了解的麻煩告知一下,非常感謝