商城項目實戰(zhàn) | 6.2 OkHttp 輕松封裝 更加靈活的調(diào)用

本文為菜鳥窩作者劉婷的連載伦乔∮碌酰”商城項目實戰(zhàn)”系列來聊聊仿”京東淘寶的購物商城”如何實現(xiàn)辫愉。
140套Android優(yōu)秀開源項目源碼,領(lǐng)取地址:http://mp.weixin.qq.com/s/afPGHqfdiApALZqHsXbw-A
或歡迎勾搭運營小姐姐(微信 id:BT474849)免費領(lǐng)取哦~

在前面的文章《商城項目實戰(zhàn) | 6.1 OkHttp 的詳細(xì)介紹 網(wǎng)絡(luò)請求更加簡單》中已經(jīng)詳細(xì)介紹了 OkHttp 的基本屬性和使用方法了族铆,OkHttp 開源框架給開發(fā)者帶來了很多的便利,使用非常方便哭尝,但是在項目中網(wǎng)絡(luò)請求一般到處都要使用到哥攘,如果總是重復(fù)相同的代碼,首先代碼不夠簡潔,其次效率也變低了不少莉掂。代碼的簡潔性是一個好的開發(fā)者所不可缺少的開發(fā)特點胳岂,如何使得代碼更為的簡潔呢蚓耽?那就要學(xué)會封裝了,本文主要是針對 OkHttp 框架使用的封裝茉兰,學(xué)會了這個,封裝其他的也都不是問題了欣簇。

封裝的意義和好處

1. 封裝的概念

封裝從字面上來理解就是包裝的意思规脸,專業(yè)點就是信息隱藏,是指利用抽象數(shù)據(jù)類型將數(shù)據(jù)和基于數(shù)據(jù)的操作封裝在一起熊咽,使其構(gòu)成一個不可分割的獨立實體莫鸭,數(shù)據(jù)被保護(hù)在抽象數(shù)據(jù)類型的內(nèi)部,盡可能地隱藏內(nèi)部的細(xì)節(jié)横殴,只保留一些對外接口使之與外部發(fā)生聯(lián)系被因。系統(tǒng)的其他對象只能通過包裹在數(shù)據(jù)外面的已經(jīng)授權(quán)的操作來與這個封裝的對象進(jìn)行交流和交互。封裝是面向?qū)ο蟮娜筇卣髦簧缆兀褪菍㈩惖臓顟B(tài)信息隱藏在類的內(nèi)部梨与,不允許外部程序直接訪問,而通過該類提供的方法來實現(xiàn)對隱藏信息的操作和訪問文狱。

2. 封裝的好處

  1. 1.良好的封裝能夠減少耦合粥鞋。
  2. 2.不必關(guān)心具體的實現(xiàn)。
  3. 3.控制用戶對類的修改和訪問數(shù)據(jù)的程度如贷,提高安全性陷虎。
  4. 4.可以方便的加入存取控制語句,限制不合理操作杠袱。
  5. 5.代碼更加容易被理解和維護(hù)尚猿。

OkHttp 的封裝

已經(jīng)了解了封裝的意義和所帶來的好處,更加要把封裝應(yīng)用到自己的代碼中去楣富,讓代碼更為得具有簡潔性和易維護(hù)性凿掂,下面開始 OkHttp 的封裝。

1. 封裝 OkHttp 要實現(xiàn)什么

在做一件事情之前纹蝴,都要考慮為什么要這樣做庄萎,在寫一個項目之前,都要考慮這個項目的需求塘安,在寫一行代碼之前糠涛,所要想的是要實現(xiàn)怎么的功能,同樣的兼犯,在封裝之前忍捡,要思考封裝可以實現(xiàn)什么集漾,可以先列舉出來,下面是我列舉的希望封裝 OkHttp 能夠?qū)崿F(xiàn)的功能砸脊。

  1. 1.OkHttpClient 可以不重復(fù)寫具篇,直接簡單調(diào)用 Get 和 Post 方法,尤其是 post 調(diào)用時傳遞參數(shù)希望可以一步搞定凌埂。
  2. 2.返回的數(shù)據(jù)可以直接拿來使用驱显,不要自己再過多的處理和序列化。
  3. 3.有些時候加載數(shù)據(jù)希望顯示提示加載框瞳抓,數(shù)據(jù)加載完后顯示框 dismiss埃疫,但是有的時候又不希望提示加載框顯示出來。
  4. 4.網(wǎng)絡(luò)請求失敗后挨下,可以提示錯誤信息熔恢,同時對于請求成功和請求失敗后的操作可以自己再擴(kuò)展。

按照這樣的想法臭笆,一步一步來實現(xiàn) OkHttp 的封裝叙淌。

**2. 實現(xiàn)封裝 OkHttp **

在列舉的所要實現(xiàn)的功能中,希望可以返回的數(shù)據(jù)可以直接拿來使用愁铺,另外還要顯示加載的提示框鹰霍,那么在封裝的 OkHttp 中一定需要對數(shù)據(jù)進(jìn)行處理,比如 Json 類型的數(shù)據(jù)茵乱,我們就用 Gson 框架來處理了茂洒,而提示對話框的話,這里也使用一款開源的框架 SpotsDialog 瓶竭。

2.1 添加 Gradle 依賴

因為使用的工具為 Android Studio 2.3.0督勺,所使用的集成工具為 gradle,所以在使用第三方開源框架時斤贰,添加依賴是永遠(yuǎn)必不可少的智哀。

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.2.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.1'
    testCompile 'junit:junit:4.12'
    compile 'com.daimajia.slider:library:1.1.5@aar'
    compile 'com.squareup.picasso:picasso:2.5.2'
    compile 'com.nineoldandroids:library:2.4.0'
    compile 'com.android.support:support-v4:25.2.0'
    compile 'com.android.support:recyclerview-v7:25.2.0'
    compile 'com.android.support:cardview-v7:25.2.0'
    compile 'com.squareup.okhttp3:okhttp:3.6.0'
    compile 'com.google.code.gson:gson:2.8.0'
    compile 'com.github.d-max:spots-dialog:0.7'
}

2.2 封裝 Request

在使用 OkHttp 的時候,所調(diào)用的 get 和 post 方法雖然有所不同荧恍,但是其實很多的代碼還是重復(fù)的瓷叫,有必要把重復(fù)的代碼提取出來,另外這里主要是講解異步請求的方法送巡,所以封裝的也是針對于異步網(wǎng)絡(luò)請求的摹菠。
創(chuàng)建好 OkHttpHelper 類,添加構(gòu)造函數(shù) OkHttpHelper()骗爆,首先就是在這里創(chuàng)建 OkHttpClient次氨,進(jìn)行簡單的設(shè)置,同時 OkHttpHelper 的調(diào)用也要寫好來摘投,后期要使用 OkHttpHelper 時煮寡,就直接調(diào) getInstance() 方法就好了屉佳。

private  static  OkHttpHelper mInstance;
static {
            mInstance = new OkHttpHelper();
        }
public static  OkHttpHelper getInstance(){
            return  mInstance;
        }
private OkHttpHelper(){
       mHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10,TimeUnit.SECONDS)
                    .writeTimeout(30,TimeUnit.SECONDS)
                    .build();
}

至于 get 和 post 還是需要分區(qū)下的,直接使用枚舉洲押。

enum  HttpMethodType{
            GET,
            POST,
        }

然后就是 Request 的創(chuàng)建,根據(jù) get 和 post 的不同來相應(yīng)創(chuàng)建 Request 圆凰,后期我們可以直接傳入?yún)^(qū)分參數(shù) HttpMethodType 調(diào)用這一個函數(shù)就可以了杈帐。代碼如下。

private  Request buildRequest(String url,HttpMethodType methodType,Map<String,Object> params){


            Request.Builder builder = new Request.Builder()
                    .url(url);

            if (methodType == HttpMethodType.POST){
                RequestBody body = builderFormData(params);
                builder.post(body);
            }
            else if(methodType == HttpMethodType.GET){

                url = buildUrlParams(url,params);
                builder.url(url);

                builder.get();
            }


            return builder.build();
        }

        private RequestBody builderFormData(Map<String,Object> params){

            FormBody.Builder builder = new FormBody.Builder();

            if(params !=null){
                for (Map.Entry<String,Object> entry :params.entrySet() ){

                    builder.add(entry.getKey(),entry.getValue()==null?"":entry.getValue().toString());
                }
            }

       private   String buildUrlParams(String url ,Map<String,Object> params) {

        if(params == null)
            params = new HashMap<>(1);
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            sb.append(entry.getKey() + "=" + (entry.getValue()==null?"":entry.getValue().toString()));
            sb.append("&");
        }
        String s = sb.toString();
        if (s.endsWith("&")) {
            s = s.substring(0, s.length() - 1);
        }

        if(url.indexOf("?")>0){
            url = url +"&"+s;
        }else{
            url = url +"?"+s;
        }

        return url;
    }

2.3 封裝 Callback

前面已經(jīng)寫好了 Request 了专钉,下面就是要考慮網(wǎng)絡(luò)請求之后的回調(diào)了挑童,主要是針對于網(wǎng)絡(luò)請求成功、網(wǎng)絡(luò)請求失敗以及請求成功但是遇到錯誤了的情況跃须,定義 abstract 抽象類 BaseCallback站叼。

public abstract class BaseCallback <T> {


    public   Type mType;

    static Type getSuperclassTypeParameter(Class<?> subclass)
    {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class)
        {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameterized = (ParameterizedType) superclass;
        return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
    }


    public BaseCallback()
    {
        mType = getSuperclassTypeParameter(getClass());
    }



    public  abstract void onBeforeRequest(Request request);


    public abstract  void onFailure(Request request, Exception e) ;


    /**
     *請求成功時調(diào)用此方法
     * @param response
     */
    public abstract  void onResponse(Response response);

    /**
     *
     * 狀態(tài)碼大于200,小于300 時調(diào)用此方法
     * @param response
     * @param t
     * @throws IOException
     */
    public abstract void onSuccess(Response response,T t) ;

    /**
     * 狀態(tài)碼400菇民,404尽楔,403,500等時調(diào)用此方法
     * @param response
     * @param code
     * @param e
     */
    public abstract void onError(Response response, int code,Exception e) ;

}

2.4 對獲取的數(shù)據(jù)處理和序列化

請求之后就是要處理獲取到的數(shù)據(jù)了第练,如果是 String 類型的數(shù)據(jù)阔馋,就不用過多處理,但是如果是 Json 格式的數(shù)據(jù)娇掏,就要序列化了呕寝,處理數(shù)據(jù)主要是請求成功并且沒有出現(xiàn)錯誤的時候?qū)Λ@取的數(shù)據(jù)進(jìn)行處理,所以也要用到之前寫好的 BaseCallback 了婴梧。
當(dāng)然先要在構(gòu)造函數(shù) OkHttpHelper() 中初始化 Gson下梢。

Gson mGson = new Gson();
public  void request(final Request request,final  BaseCallback callback){

            callback.onBeforeRequest(request);

            mHttpClient.newCall(request).enqueue(new Callback() {

                @Override
                public void onFailure(Call call, IOException e) {
                    callbackFailure(callback, request, e);
                }

                @Override
                public void onResponse(Call call,Response response) throws IOException {
                    callbackResponse(callback,response);

                    if(response.isSuccessful()) {

                        String resultStr = response.body().string();

                        Log.d(TAG, "result=" + resultStr);

                        if (callback.mType == String.class){
                            callbackSuccess(callback,response,resultStr);
                        }
                        else {
                            try {

                                Object obj = mGson.fromJson(resultStr, callback.mType);
                                callbackSuccess(callback,response,obj);
                            }
                            catch (com.google.gson.JsonParseException e){ // Json解析的錯誤
                                callback.onError(response,response.code(),e);
                            }
                        }
                    }
                    else {
                        callbackError(callback,response,null);
                    }

                }
            });


        }

2.5 Handler 來幫忙

在使用網(wǎng)絡(luò)請求開線程的時候一定要注意一個問題,那就是對 View 的操作塞蹭,必須在主線程中孽江,所以就需要 Handler 來幫忙了。
當(dāng)然先要在構(gòu)造函數(shù) OkHttpHelper() 中初始化 Handler浮还。

Handler mHandler = new Handler(Looper.getMainLooper());
private void callbackSuccess(final  BaseCallback callback , final Response response, final Object obj ){

            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onSuccess(response, obj);
                }
            });
        }


        private void callbackError(final  BaseCallback callback , final Response response, final Exception e ){

            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    callback.onError(response,response.code(),e);
                }
            });
        }



    private void callbackFailure(final  BaseCallback callback , final Request request, final IOException e ){

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onFailure(request,e);
            }
        });
    }


    private void callbackResponse(final  BaseCallback callback , final Response response ){

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onResponse(response);
            }
        });
    }

2.6 加入提示加載框

之前列舉的功能中希望有時候可以顯示加載框竟坛,有時候不顯示,所以這里就需要擴(kuò)展下之前的 BaseCallback钧舌,寫一個帶有提示加載框的 SpotsCallBack担汤,并且在里面處理提示框的顯示和隱藏。

public abstract class SpotsCallBack<T> extends SimpleCallback<T> {



    private  SpotsDialog mDialog;

    public SpotsCallBack(Context context){
        super(context);

        initSpotsDialog();
    }


    private  void initSpotsDialog(){

        mDialog = new SpotsDialog(mContext,"拼命加載中...");
    }

    public  void showDialog(){
        mDialog.show();
    }

    public  void dismissDialog(){
        mDialog.dismiss();
    }


    public void setLoadMessage(int resId){
        mDialog.setMessage(mContext.getString(resId));
    }


    @Override
    public void onBeforeRequest(Request request) {

        showDialog();
    }

    @Override
    public void onResponse(Response response) {
        dismissDialog();
    }

    @Override
    public void onFailure(Request request, Exception e) {
        dismissDialog();
        super.onFailure(request, e);
    }

    @Override
    public void onError(Response response, int code, Exception e) {
        dismissDialog();
    }
}

2.7 寫好 get 和 post 的調(diào)用方法

所需要封裝的都已經(jīng)封裝好了洼冻,現(xiàn)在就是要在 OkHttpHelper 中寫好相應(yīng)的 get 和 post 的調(diào)用方法崭歧,也就是函數(shù)。

public void get(String url,Map<String,Object> param,BaseCallback callback){


            Request request = buildGetRequest(url,param);

            request(request,callback);

        }

        public void get(String url,BaseCallback callback){

            get(url,null,callback);
        }


        public void post(String url,Map<String,Object> param, BaseCallback callback){

            Request request = buildPostRequest(url,param);
            request(request,callback);
        }

到這里就基本實現(xiàn)了我之前列舉的功能了撞牢,如果還需要其他的功能的話率碾,可以自行封裝其他的叔营。

使用封裝好的 OkHttp

已經(jīng)封裝好的 OkHttp 要實際運用起來,看下是不是優(yōu)化了很多所宰,在之前的文章《商城項目實戰(zhàn) | 6.1 OkHttp 的詳細(xì)介紹 網(wǎng)絡(luò)請求更加簡單》中使用 OkHttp 來實現(xiàn)炫酷輪播廣告绒尊,現(xiàn)在我們就用封裝好的 OkHttp 來實現(xiàn)同樣的功能。

下面是封裝前的網(wǎng)絡(luò)請求方法仔粥,這里使用的是 get 請求婴谱。

private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case INIT_SLIDER_TYPE:
                    initSlider();
                    break;
            }
        }
    };

private void getBannerData() {
    String url ="http://112.124.22.238:8081/course_api/banner/query?type=1";
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Toast.makeText(getActivity(),e.getMessage().toString(),Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if(response.isSuccessful()){
                    Type type = new TypeToken<List<BannerInfo>>(){}.getType();
                    Gson gson = new Gson();
                    List<BannerInfo> list= gson.fromJson(response.body().string(),type);
                    for (BannerInfo bannerInfo:list)
                    {
                        listBanner.add(bannerInfo);
                    }
                    handler.sendEmptyMessage(INIT_SLIDER_TYPE);
                }else {
                    Toast.makeText(getActivity(),"IOException",Toast.LENGTH_SHORT).show();
                }
            }
        });
}

下面是封裝后的網(wǎng)絡(luò)請求處理。

private void getBannerData() {
        String url ="http://112.124.22.238:8081/course_api/banner/query?type=1";
        httpHelper.get(url, new SpotsCallBack<List<BannerInfo>>(getActivity()){


            @Override
            public void onSuccess(Response response, List<BannerInfo> banners) {

                listBanner = banners;
                initSlider();
            }

            @Override
            public void onError(Response response, int code, Exception e) {
                Toast.makeText(getActivity(),code+e.toString(),Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(Request request, Exception e) {
                super.onFailure(request, e);
                Toast.makeText(getActivity(),e.toString(),Toast.LENGTH_SHORT).show();
            }
        });
}

還是使用的 get 請求方法躯泰,但是感覺代碼明顯簡潔了不少谭羔,也更加的規(guī)整,同時調(diào)用也方便了很多麦向。

效果圖

最后還是運行下代碼瘟裸,獲取到效果圖。
[圖片上傳失敗...(image-d9fec5-1565145693331)]

效果圖和之前是一樣的诵竭,但是方法已經(jīng)簡便了很多话告,封裝可以給我們帶來很多的便利,也讓我們更加靈活的使用 OkHttp 了秀撇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末超棺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子呵燕,更是在濱河造成了極大的恐慌棠绘,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件再扭,死亡現(xiàn)場離奇詭異氧苍,居然都是意外死亡,警方通過查閱死者的電腦和手機泛范,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門让虐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人罢荡,你說我怎么就攤上這事赡突。” “怎么了区赵?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵惭缰,是天一觀的道長。 經(jīng)常有香客問我笼才,道長漱受,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任骡送,我火速辦了婚禮昂羡,結(jié)果婚禮上絮记,老公的妹妹穿的比我還像新娘。我一直安慰自己虐先,他們只是感情好怨愤,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛹批,像睡著了一般憔四。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上般眉,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音潜支,去河邊找鬼甸赃。 笑死,一個胖子當(dāng)著我的面吹牛冗酿,可吹牛的內(nèi)容都是我干的埠对。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼裁替,長吁一口氣:“原來是場噩夢啊……” “哼项玛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弱判,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤襟沮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昌腰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體开伏,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年遭商,在試婚紗的時候發(fā)現(xiàn)自己被綠了固灵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡劫流,死狀恐怖巫玻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祠汇,我是刑警寧澤仍秤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站座哩,受9級特大地震影響徒扶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜根穷,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一姜骡、第九天 我趴在偏房一處隱蔽的房頂上張望导坟。 院中可真熱鬧,春花似錦圈澈、人聲如沸惫周。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽递递。三九已至,卻和暖如春啥么,著一層夾襖步出監(jiān)牢的瞬間登舞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工悬荣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留菠秒,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓氯迂,卻偏偏與公主長得像践叠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嚼蚀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

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