retrofit2調(diào)用webservice-3.攔截器,細節(jié)優(yōu)化

retrofit2調(diào)用webservice請求一共3篇
一:工具
二:通過retrofit2 進行soap請求
三:攔截器,通過retrofit2 攔截器拼接入?yún)⒑瓦^濾出返回值,使soap請求更趨向于http請求

源碼地址:https://github.com/Frank1213/retrofit-soap/blob/master/WebServiceSOAP/app/src/main/java/linc/ps/MainActivity.java

先優(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了一次

Paste_Image.png

轉換的方法是調(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)這個。

以上問題有了解的麻煩告知一下,非常感謝

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內(nèi)容