Okhttp

android-async-http 基于httpClient封裝的網(wǎng)絡(luò)庫箫踩,android6.0后httpclient不是系統(tǒng)自帶的爱态,目前已不維護(hù),盡管Google在大部分安卓版本推薦使用HttpURLConnection境钟,但是這個(gè)類太難用了锦担,而OKhttp是一個(gè)相對(duì)成熟的網(wǎng)絡(luò)庫,在android4.4的源碼中HttpURLConnection已經(jīng)替換成OKHttp實(shí)現(xiàn)了慨削,很多大的第三方庫都支持它(fresco洞渔、retrofit)

api調(diào)用方便
OkHttpClient client = new OkHttpClient();
 
String run(String url) throws IOException {
    Request request = new Request.Builder().url(url).build();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}

通過Request的Builder輔助類,創(chuàng)建請(qǐng)求對(duì)象缚态,再傳遞給OkHttpClient執(zhí)行磁椒,Response為返回的內(nèi)容,這樣get請(qǐng)求就完成了玫芦。
如果要是實(shí)現(xiàn)post請(qǐng)求浆熔,則創(chuàng)建一個(gè)RequestBody對(duì)象,賦值給Request桥帆,則完成了post請(qǐng)求

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
     RequestBody body = RequestBody.create(JSON, json);
      Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
      Response response = client.newCall(request).execute();
    f (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}
支持各種形式的post請(qǐng)求

post提交字符串

String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

post提交流

RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }
 
      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }
 
      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

post提交文件

File file = new File("README.md");
 
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

post提交表單

RequestBody formBody = new FormEncodingBuilder()
        .add("search", "Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();
OkHttpClient 的執(zhí)行

調(diào)用execute方法是同步執(zhí)行医增,調(diào)用enqueue時(shí)異步執(zhí)行

響應(yīng)頭
private final OkHttpClient client = new OkHttpClient();
 
public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
}

post分塊請(qǐng)求

RequestBody requestBody = new MultipartBuilder()
        .type(MultipartBuilder.FORM)
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"title\""),
            RequestBody.create(null, "Square Logo"))
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"image\""),
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();
 
    Request request = new Request.Builder()
        .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();

能夠方便的設(shè)置和獲取響應(yīng)頭。

Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .build();
 
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
 
    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
支持同步和異步請(qǐng)求

enqueue發(fā)起異步請(qǐng)求老虫,execute發(fā)起同步請(qǐng)求

請(qǐng)求攔截

Okhttp支持定義各種攔截器對(duì)整個(gè)網(wǎng)絡(luò)請(qǐng)求流程進(jìn)行攔截(監(jiān)視调窍、重寫、重試調(diào)用)

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
   Request request = chain.request();

   long t1 = System.nanoTime();
   logger.info(String.format("Sending request %s on %s%n%s",
   request.url(), chain.connection(), request.headers()));

   Response response = chain.proceed(request);

   long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
   response.request().url(), (t2 - t1) / 1e6d, response.headers()));

return response;
  }
}

OkHttpClient client = new OkHttpClient.Builder()
     .addInterceptor(new LoggingInterceptor())
     .build();

可以定義多個(gè)攔截器张遭,按順序調(diào)用

緩存

Okhttp已經(jīng)內(nèi)置了緩存邓萨,使用DiskLruCache,使用緩存需要在創(chuàng)建OKhttpClient進(jìn)行配置

int cacheSize = 10 * 1024 * 1024; // 10 MiB
File cacheDirectory = new File("cache");
//出于安全性的考慮菊卷,在Android中我們推薦使用Context.getCacheDir()來作為緩存的存放路徑
if (!cacheDirectory.exists()) {
    cacheDirectory.mkdirs();
}
Cache cache = new Cache(cacheDirectory, cacheSize);
OkHttpClient newClient = okHttpClient.newBuilder()
        .Cache(cache)
        .connectTimeout(20, TimeUnit.SECONDS)
        .readTimeout(20, TimeUnit.SECONDS)
        .build();

如果服務(wù)器支持緩存缔恳,請(qǐng)求返回的Response會(huì)帶有Header:Cache-Control, max-age=xxx,Okhttp會(huì)自動(dòng)執(zhí)行緩存洁闰,如果服務(wù)器不支持歉甚,則要通過攔截Response,給其設(shè)置Cache-Control信息

public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        Response response1 = response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                //cache for 30 days
            .header("Cache-Control", "max-age=" + 3600 * 24 * 30)
                .build();
        return response1;
    }
}

OkHttpClient okHttpClient = new OkHttpClient();

OkHttpClient newClient = okHttpClient.newBuilder()
        .addNetworkInterceptor(new CacheInterceptor())
        .cache(cache)
        .connectTimeout(20, TimeUnit.SECONDS)
        .readTimeout(20, TimeUnit.SECONDS)
        .build();

ssl支持

SSL位于TCP/IP和http協(xié)議之間扑眉,他的作用
1纸泄、認(rèn)證用戶和服務(wù)器赖钞,確保數(shù)據(jù)發(fā)送到正確的客戶機(jī)和服務(wù)器;(驗(yàn)證證書)
2聘裁、加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊妊┯;(加密)
3衡便、維護(hù)數(shù)據(jù)的完整性献起,確保數(shù)據(jù)在傳輸過程中不被改變。(摘要算法)
Okhttp默認(rèn)是可以訪問通過CA認(rèn)證的https鏈接镣陕,如果是自簽名的證書谴餐,則應(yīng)用需要存放對(duì)應(yīng)的證書,并添加到okhttp設(shè)置中呆抑。

mContext = context;
        X509TrustManager trustManager;
        SSLSocketFactory sslSocketFactory;
        final InputStream inputStream;
        try {
            inputStream = mContext.getAssets().open("srca.cer"); // 得到證書的輸入流
            try {

                trustManager = trustManagerForCertificates(inputStream);//以流的方式讀入證書
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{trustManager}, null);
                sslSocketFactory = sslContext.getSocketFactory();

            } catch (GeneralSecurityException e) {
                throw new RuntimeException(e);
            }

            client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustManager)
                    .build();
        } catch (IOException e) {
            e.printStackTrace();
        }
dns支持

HTTP DNS通過將域名查詢請(qǐng)求放入http中的一種域名解析方式岂嗓,而不用系統(tǒng)自帶的libc庫去查詢運(yùn)營(yíng)商的DNS服務(wù)器,有更大的自由度鹊碍,https下不會(huì)存在任何問題摄闸,證書校驗(yàn)依然使用域名進(jìn)行校驗(yàn)。目前微信妹萨,qq郵箱、等業(yè)務(wù)均使用了HTTP DNS炫欺。
在OKhttp中乎完,提供DNS接口,實(shí)現(xiàn)DNS類品洛,配置到OKhttp中就行了
主要優(yōu)點(diǎn):
能夠準(zhǔn)確地將站點(diǎn)解析到離用戶最近的CDN站點(diǎn)树姨,方便進(jìn)行流量調(diào)度
解決部分運(yùn)營(yíng)商DNS無法解析國(guó)外站點(diǎn)的問題
TCP在一定程度可以防止UDP無校驗(yàn)導(dǎo)致的DNS欺詐(比如墻,運(yùn)營(yíng)商廣告桥状,404導(dǎo)航站)帽揪,當(dāng)然基于HTTP的話本質(zhì)還是不安全的。

static Dns HTTP_DNS =  new Dns(){
  @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException {
    //防御代碼
    if (hostname == null) throw new UnknownHostException("hostname == null");
    //dnspod提供的dns服務(wù)
    HttpUrl httpUrl = new HttpUrl.Builder().scheme("http")
        .host("119.29.29.29")
        .addPathSegment("d")
        .addQueryParameter("dn", hostname)
        .build();
    Request dnsRequest = new Request.Builder().url(httpUrl).get().build();
    try {
      String s = getHTTPDnsClient().newCall(dnsRequest).execute().body().string();
      //避免服務(wù)器掛了卻無法查詢DNS
      if (!s.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) {
        return Dns.SYSTEM.lookup(hostname);
      }
      return Arrays.asList(InetAddress.getAllByName(s));
    } catch (IOException e) {
      return Dns.SYSTEM.lookup(hostname);
    }
  }
};

static public synchronized OkHttpClient getClient() {
  if (client == null) {
    final File cacheDir = GlobalContext.getInstance().getExternalCacheDir();
    client = new OkHttpClient.Builder().addNetworkInterceptor(getLogger())
        .cache(new Cache(new File(cacheDir, "okhttp"), 60 * 1024 * 1024))
        .dispatcher(getDispatcher())
        //配置DNS查詢實(shí)現(xiàn)
        .dns(HTTP_DNS)
        .build();
  }
  return client;
}
支持連接池

Okhttp支持5個(gè)并發(fā)KeepAlive辅斟,默認(rèn)鏈路生命為5分鐘(鏈路空閑后转晰,保持存活的時(shí)間)。
socket連接每次需要3次握手士飒、釋放要2次或4次查邢,當(dāng)訪問復(fù)雜網(wǎng)絡(luò)時(shí),延時(shí)將成為非常重要的因素酵幕,使用連接池的好處就是優(yōu)化網(wǎng)絡(luò)性能扰藕,對(duì)于延遲降低與速度提升的有非常重要的作用。
KeepAlive缺點(diǎn)則是芳撒,在提高了單個(gè)客戶端性能的同時(shí)邓深,復(fù)用卻阻礙了其他客戶端的鏈路速度未桥。

相關(guān)資料

OkHttp使用教程
Okhttp使用指南與源碼分析
Okhttp-wiki 之 Interceptors 攔截器
OkHttp 3.x 源碼解析之Interceptor 攔截器
OkHttp攔截器的實(shí)現(xiàn)原理
使用okHttp支持https
Android使用OkHttp請(qǐng)求自簽名的https網(wǎng)站
OkHttp3應(yīng)用HTTP DNS的實(shí)現(xiàn)
Android OkHttp實(shí)現(xiàn)HttpDns的最佳實(shí)踐(非攔截器)
Android網(wǎng)絡(luò)編程(七)源碼解析OkHttp前篇請(qǐng)求網(wǎng)絡(luò)
OkHttp3源碼分析復(fù)用連接池

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芥备,隨后出現(xiàn)的幾起案子冬耿,更是在濱河造成了極大的恐慌,老刑警劉巖门躯,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淆党,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡讶凉,警方通過查閱死者的電腦和手機(jī)染乌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂讯,“玉大人荷憋,你說我怎么就攤上這事『滞” “怎么了勒庄?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瘫里。 經(jīng)常有香客問我实蔽,道長(zhǎng),這世上最難降的妖魔是什么谨读? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任局装,我火速辦了婚禮,結(jié)果婚禮上劳殖,老公的妹妹穿的比我還像新娘铐尚。我一直安慰自己,他們只是感情好哆姻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布宣增。 她就那樣靜靜地躺著,像睡著了一般矛缨。 火紅的嫁衣襯著肌膚如雪爹脾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天箕昭,我揣著相機(jī)與錄音誉简,去河邊找鬼。 笑死盟广,一個(gè)胖子當(dāng)著我的面吹牛闷串,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筋量,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼烹吵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼碉熄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肋拔,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤锈津,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后凉蜂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琼梆,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年窿吩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茎杂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纫雁,死狀恐怖煌往,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轧邪,我是刑警寧澤刽脖,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站忌愚,受9級(jí)特大地震影響曲管,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硕糊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一院水、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧癌幕,春花似錦、人聲如沸昧穿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽时鸵。三九已至胶逢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饰潜,已是汗流浹背初坠。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彭雾,地道東北人碟刺。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像薯酝,于是被迫代替她去往敵國(guó)和親半沽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爽柒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • 參考Android網(wǎng)絡(luò)請(qǐng)求心路歷程Android Http接地氣網(wǎng)絡(luò)請(qǐng)求(HttpURLConnection) 一...
    合肥黑閱讀 21,235評(píng)論 7 63
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)者填,斷路器浩村,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 這篇文章主要講 Android 網(wǎng)絡(luò)請(qǐng)求時(shí)所使用到的各個(gè)請(qǐng)求庫的關(guān)系,以及 OkHttp3 的介紹占哟。(如理解有誤心墅,...
    小莊bb閱讀 1,145評(píng)論 0 4
  • 參考o(jì)khttp官方wiki https://github.com/square/okhttp/wiki/Call...
    WangGavin閱讀 7,144評(píng)論 0 1
  • OKHttp的使用和類關(guān)系 本文目的是對(duì)OkHttp 框架及其中的常用概念(類)做簡(jiǎn)單性介紹。 簡(jiǎn)介 OKHttp...
    嘎啦果安卓獸閱讀 3,379評(píng)論 1 10