Android網(wǎng)絡(luò)編程(五)OkHttp2.x用法全解析

前言

講完了Volley,我們接下來看看目前比較火的網(wǎng)絡(luò)框架OkHttp践樱, 它處理了很多網(wǎng)絡(luò)疑難雜癥:會(huì)從很多常用的連接問題中自動(dòng)恢復(fù)踩萎。如果您的服務(wù)器配置了多個(gè)IP地址,當(dāng)?shù)谝粋€(gè)IP連接失敗的時(shí)候登夫,OkHttp會(huì)自動(dòng)嘗試下一個(gè)IP广匙,此外OkHttp還處理了代理服務(wù)器問題和SSL握手失敗問題。

1.使用前準(zhǔn)備

eclipse引入jar包地址:
okhttp-2.7.5.jar
okio-1.7.0.jar

Android Studio 配置gradle:

  compile 'com.squareup.okhttp:okhttp:2.7.5'
  compile 'com.squareup.okio:okio:1.7.0'

2.異步GET請(qǐng)求

最簡(jiǎn)單的get請(qǐng)求恼策,老規(guī)矩請(qǐng)求百度:

      private void getAsynHttp() {
        //創(chuàng)建okHttpClient對(duì)象
        OkHttpClient mOkHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                String str = response.body().string();
                Log.i("wangshu", str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplication(), "請(qǐng)求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

運(yùn)行程序log打印出來的是百度首頁的html文件鸦致,基本的步驟很簡(jiǎn)單,就是創(chuàng)建OkHttpClient涣楷、Request和Call分唾,最后調(diào)用Call的enqueue()方法。但是每次這么寫肯定是很麻煩狮斗,肯定是要進(jìn)行封裝的绽乔。需要注意的是onResponse回調(diào)并不是在UI線程。

3.同步GET請(qǐng)求

 private String getSyncHttp() throws IOException{
        OkHttpClient mOkHttpClient = new OkHttpClient();
        //創(chuàng)建請(qǐng)求Request
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        Response mResponse=call.execute();
        if (mResponse.isSuccessful()) {
            return mResponse.body().string();
        } else {
            throw new IOException("Unexpected code " + mResponse);
        }
    }
    

同步Get請(qǐng)求和異步調(diào)用區(qū)別就是調(diào)用了call的execute()方法碳褒。

4.異步POST請(qǐng)求

    private void postAsynHttp() {
        OkHttpClient mOkHttpClient = new OkHttpClient();
        RequestBody formBody = new FormEncodingBuilder()
                .add("size", "10")
                .build();

        Request request = new Request.Builder()
                .url("http://api.1-blog.com/biz/bizserver/article/list.do")
                .post(formBody)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {

            }

            @Override
            public void onResponse(Response response) throws IOException {
                String str = response.body().string();
                Log.i("wangshu", str);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "請(qǐng)求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

post與get不同的就是要要?jiǎng)?chuàng)建RequestBody并傳進(jìn)Request中折砸,同樣onResponse回調(diào)不是在UI線程看疗。

5.請(qǐng)求緩存設(shè)置

首先我們?cè)O(shè)置緩存路徑和大小并設(shè)置給OkHttpClient:

 mOkHttpClient = new OkHttpClient();
 File sdcache = getExternalCacheDir();
 int cacheSize = 10 * 1024 * 1024;
 mOkHttpClient.setCache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
 

接下來異步GET請(qǐng)求baidu:

 private void getAsynHttp() {
        //創(chuàng)建請(qǐng)求Request
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                if (null != response.cacheResponse()) {
                    String str = response.cacheResponse().toString();
                    Log.i("wangshu", "cache---" + str);
                } else {
                    response.body().string();
                    String str=response.networkResponse().toString();
                    Log.i("wangshu", "network---" + str);
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), "請(qǐng)求成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }

第一次請(qǐng)求會(huì)請(qǐng)求網(wǎng)絡(luò)得到數(shù)據(jù),第二次以及后面的請(qǐng)求則會(huì)從緩存中取出數(shù)據(jù):


當(dāng)然也有種情況是有的請(qǐng)求每次都需要最新的數(shù)據(jù)鞍爱,則在創(chuàng)建Request鹃觉,來設(shè)置cacheControl為“CacheControl.FORCE_NETWORK”挪蹭,用來表示請(qǐng)求會(huì)一直請(qǐng)求網(wǎng)絡(luò)得到數(shù)據(jù):

  final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();

運(yùn)行程序結(jié)果為:

6.設(shè)置超時(shí)時(shí)間

另外我們也需要設(shè)置超時(shí)的時(shí)間用來處理各種網(wǎng)絡(luò)超時(shí)的情況择葡,超時(shí)的原因可能是網(wǎng)絡(luò)問題也可能是服務(wù)器響應(yīng)慢等問題凰锡,OkHttp當(dāng)然不會(huì)忽略這一點(diǎn)蚜退,它支持連接降淮、讀取和寫入超時(shí)的時(shí)間設(shè)置:

        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(20, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(20, TimeUnit.SECONDS);

7.取消請(qǐng)求

使用call.cancel()可以立即停止掉一個(gè)正在執(zhí)行的call有鹿。如果一個(gè)線程正在寫請(qǐng)求或者讀響應(yīng)摔握,將會(huì)引發(fā)IOException糕韧。當(dāng)用戶離開一個(gè)應(yīng)用時(shí)或者跳到其他界面時(shí)翼闹,使用Call.cancel()可以節(jié)約網(wǎng)絡(luò)資源斑鼻,另外不管同步還是異步的call都可以取消。
也可以通過tags來同時(shí)取消多個(gè)請(qǐng)求猎荠。當(dāng)你構(gòu)建一請(qǐng)求時(shí)坚弱,使用RequestBuilder.tag(tag)來分配一個(gè)標(biāo)簽。之后你就可以用OkHttpClient.cancel(tag)來取消所有帶有這個(gè)tag的call关摇。

為了模擬這個(gè)場(chǎng)景我們首先創(chuàng)建一個(gè)定時(shí)的線程池:

   private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

接下來的代碼為:

 private  void cancel(){
        final Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();
        Call call=null;
        call = mOkHttpClient.newCall(request);
        final Call finalCall = call;
        //100毫秒后取消call
        executor.schedule(new Runnable() {
            @Override public void run() {
                finalCall.cancel();
            }
        }, 100, TimeUnit.MILLISECONDS);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
            }

            @Override
            public void onResponse(final Response response) {
                if (null != response.cacheResponse()) {
                    String str = response.cacheResponse().toString();
                    Log.i("wangshu", "cache---" + str);
                } else {
                    try {
                        response.body().string();

                    } catch (IOException e) {
                        Log.i("wangshu", "IOException");
                        e.printStackTrace();
                    }
                    String str = response.networkResponse().toString();
                    Log.i("wangshu", "network---" + str);
                }
            }
        });
           Log.i("wangshu", "是否取消成功"+call.isCanceled());
    }

100毫秒后調(diào)用call.cancel()荒叶,為了能讓請(qǐng)求耗時(shí),我們?cè)O(shè)置每次請(qǐng)求都要請(qǐng)求網(wǎng)絡(luò)输虱,運(yùn)行程序并且不斷的快速點(diǎn)擊發(fā)送請(qǐng)求按鈕:


很明顯每次cancel()都失敗了些楣,仍舊成功的訪問了網(wǎng)絡(luò),在cancel()時(shí)已經(jīng)有讀寫操作了所以會(huì)報(bào)IOException宪睹。每隔100毫秒來調(diào)用call.cancel()顯然時(shí)間間隔太長(zhǎng)愁茁,我們?cè)O(shè)置為1毫秒并不斷的快速的點(diǎn)擊發(fā)送請(qǐng)求按鈕:


沒有請(qǐng)求網(wǎng)絡(luò)的log,幾乎每次都取消成功了亭病。

8.關(guān)于封裝

如果每次請(qǐng)求網(wǎng)絡(luò)都需要寫重復(fù)的代碼絕對(duì)是令人頭疼的鹅很,網(wǎng)上也有很多對(duì)OkHttp封裝的優(yōu)秀開源項(xiàng)目,功能也非常強(qiáng)大罪帖,封裝的意義就在于更加方便的使用道宅,具有拓展性,但是對(duì)OkHttp封裝最需要解決的是以下的兩點(diǎn):

  1. 避免重復(fù)代碼調(diào)用
  2. 將請(qǐng)求結(jié)果回調(diào)改為UI線程

根據(jù)以上兩點(diǎn)胸蛛,我們也簡(jiǎn)單封裝一下污茵,在此只是舉個(gè)例子,如果想要使用OkHttp封裝的開源庫葬项,推薦使用OkHttpFinal泞当。
首先呢我們寫一個(gè)抽象類用于請(qǐng)求回調(diào):

public abstract class ResultCallback<T>
{
    public abstract void onError(Request request, Exception e);

    public abstract void onResponse(Response response);
}

接下來封裝OkHttp,并實(shí)現(xiàn)了異步GET請(qǐng)求:

public class OkHttpEngine {
    private static OkHttpEngine mInstance;
    private OkHttpClient mOkHttpClient;
    private Handler mHandler;

    public static OkHttpEngine getInstance() {
        if (mInstance == null) {
            synchronized (OkHttpEngine.class) {
                if (mInstance == null) {
                    mInstance = new OkHttpEngine();
                }
            }
        }
        return mInstance;
    }

    private OkHttpEngine() {
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(20, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(20, TimeUnit.SECONDS);
        mHandler = new Handler();

    }

    public OkHttpEngine setCache(Context mContext) {
        File sdcache = mContext.getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        mOkHttpClient.setCache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
        return mInstance;
    }

    /**
     * 異步get請(qǐng)求
     * @param url
     * @param callback
     */
    public void getAsynHttp(String url, ResultCallback callback) {

        final Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = mOkHttpClient.newCall(request);
        dealResult(call, callback);

    }


    private void dealResult(Call call, final ResultCallback callback) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                sendFailedCallback(request, e, callback);
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                sendSuccessCallback(response, callback);
            }


            private void sendSuccessCallback(final Response object, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null) {
                            callback.onResponse(object);
                        }
                    }
                });
            }

            private void sendFailedCallback(final Request request, final Exception e, final ResultCallback callback) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (callback != null)
                            callback.onError(request, e);
                    }
                });
            }

        });
    }
}

原理很簡(jiǎn)單就是民珍,寫一個(gè)雙重檢查模式的單例襟士,不了解雙重檢查模式的請(qǐng)查看設(shè)計(jì)模式之單例模式的七種寫法這篇文章盗飒。在開始創(chuàng)建的時(shí)候配置好OkHttpClient,在請(qǐng)求網(wǎng)絡(luò)的時(shí)候用Handler將請(qǐng)求的結(jié)果回調(diào)給UI線程陋桂。

最后調(diào)用這個(gè)OkHttpEngine的getAsynHttp()方法:

  OkHttpEngine.getInstance().getAsynHttp("http://www.baidu.com", new ResultCallback() {
            @Override
            public void onError(Request request, Exception e) {

            }

            @Override
            public void onResponse(Response response) {
                String str = response.networkResponse().toString();
                Log.i("wangshu", str);
                Toast.makeText(getApplicationContext(), "請(qǐng)求成功", Toast.LENGTH_SHORT).show();
            }
        });

使用起來簡(jiǎn)單多了逆趣,而且請(qǐng)求結(jié)果回調(diào)是在UI線程的。下一篇我們會(huì)講到OkHttp3嗜历,來看看它與OkHttp2.x之間的使用方式上有什么區(qū)別宣渗。

github源碼下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梨州,隨后出現(xiàn)的幾起案子痕囱,更是在濱河造成了極大的恐慌,老刑警劉巖暴匠,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞍恢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡每窖,警方通過查閱死者的電腦和手機(jī)帮掉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窒典,“玉大人旭寿,你說我怎么就攤上這事〕绨埽” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵肩祥,是天一觀的道長(zhǎng)后室。 經(jīng)常有香客問我,道長(zhǎng)混狠,這世上最難降的妖魔是什么岸霹? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮将饺,結(jié)果婚禮上贡避,老公的妹妹穿的比我還像新娘。我一直安慰自己予弧,他們只是感情好刮吧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掖蛤,像睡著了一般杀捻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚓庭,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天致讥,我揣著相機(jī)與錄音仅仆,去河邊找鬼。 笑死垢袱,一個(gè)胖子當(dāng)著我的面吹牛墓拜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播请契,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼咳榜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了姚糊?” 一聲冷哼從身側(cè)響起贿衍,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎救恨,沒想到半個(gè)月后贸辈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肠槽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年擎淤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸仙。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘴拢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寂纪,到底是詐尸還是另有隱情席吴,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布捞蛋,位于F島的核電站孝冒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拟杉。R本人自食惡果不足惜庄涡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搬设。 院中可真熱鬧穴店,春花似錦、人聲如沸拿穴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽默色。三九已至斜棚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弟蚀。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工蚤霞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人义钉。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓昧绣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親捶闸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夜畴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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