【Android】網(wǎng)絡(luò)(二)網(wǎng)絡(luò)框架OKHttp

高性能網(wǎng)絡(luò)框架OKHttp

出現(xiàn)背景

在okhttp出現(xiàn)以前愉棱,android上發(fā)起網(wǎng)絡(luò)請(qǐng)求要么使用系統(tǒng)自帶的HttpClient肌稻、HttpURLConnection、要么使用google開源的Volley、要么使用第三方開源的AsyncHttpClient, 隨著互聯(lián)網(wǎng)的發(fā)展徘意,APP的業(yè)務(wù)發(fā)展也越來越復(fù)雜,APP的網(wǎng)絡(luò)請(qǐng)求數(shù)量急劇增加轩褐,但是上述的網(wǎng)絡(luò)請(qǐng)求框架均存在難以性能和并發(fā)數(shù)量的限制

OkHttp流行得益于它的良好的架構(gòu)設(shè)計(jì)椎咧,強(qiáng)大的攔截器(intercepts)使得操縱網(wǎng)絡(luò)十分方便;OkHttp現(xiàn)在已經(jīng)得到Google官方認(rèn)可,大量的app都采用OkHttp做網(wǎng)絡(luò)請(qǐng)求勤讽,其源碼詳見OkHttp Github蟋座。

也得益于強(qiáng)大的生態(tài),大量的流行庫都以OkHttp作為底層網(wǎng)絡(luò)框架或提供支持脚牍,比如Retrofit向臀、GlideFresco诸狭、Moshi券膀、Picasso等。

當(dāng)OKhttp面世之后驯遇,瞬間成為各個(gè)公司的開發(fā)者的新寵三娩,常年霸占github star榜單,okhttp可以說是為高效而生,迎合了互聯(lián)網(wǎng)高速發(fā)展的需要

特點(diǎn)

  1. 同時(shí)支持HTTP1.1與支持HTTP2.0妹懒;
  2. 同時(shí)支持同步與異步請(qǐng)求雀监;
  3. 同時(shí)具備HTTP與WebSocket功能;
  4. 擁有自動(dòng)維護(hù)的socket連接池眨唬,減少握手次數(shù)会前;
  5. 擁有隊(duì)列線程池,輕松寫并發(fā)匾竿;
  6. 擁有Interceptors(攔截器)瓦宜,輕松處理請(qǐng)求與響應(yīng)額外需求(例:請(qǐng)求失敗重試、響應(yīng)內(nèi)容重定向等等)岭妖;

開始使用

在AndroidManifest.xml添加網(wǎng)絡(luò)訪問權(quán)限

<uses-permission android:name="android.permission.INTERNET" />
<application
    ...
    android:usesCleartextTraffic="true"
    ...
</application>

添加依賴

app/build.gradledependencies中添加下面的依賴

implementation("com.squareup.okhttp3:okhttp:4.9.0")

// 網(wǎng)絡(luò)請(qǐng)求日志打印
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

初始化

val client  = OkHttpClient.Builder()    //builder構(gòu)造者設(shè)計(jì)模式
         .connectTimeout(10, TimeUnit.SECONDS) //連接超時(shí)時(shí)間
         .readTimeout(10, TimeUnit.SECONDS)    //讀取超時(shí)  
         .writeTimeout(10, TimeUnit.SECONDS)  //寫超時(shí)临庇,也就是請(qǐng)求超時(shí)
         .build();

GET請(qǐng)求

同步GET請(qǐng)求

同步GET的意思是一直等待http請(qǐng)求, 直到返回了響應(yīng). 在這之間會(huì)阻塞線程, 所以同步請(qǐng)求不能在Android的主線程中執(zhí)行, 否則會(huì)報(bào)錯(cuò)NetworkMainThreadException.

val client = OkHttpClient()

fun run(url: String) {
    val request: Request = Request.Builder()
        .url(url)
        .build()
    val call =client.newCall(request)
    val response=call.execute()
    val body = response.body?.string() 
    println("get response :${body}")
}

發(fā)送同步GET請(qǐng)求很簡單:

  1. 創(chuàng)建OkHttpClient實(shí)例client
  2. 通過Request.Builder構(gòu)建一個(gè)Request請(qǐng)求實(shí)例request
  3. 通過client.newCall(request)創(chuàng)建一個(gè)Call的實(shí)例
  4. Call的實(shí)例調(diào)用execute方法發(fā)送同步請(qǐng)求
  5. 請(qǐng)求返回的response轉(zhuǎn)換為String類型返回

異步GET請(qǐng)求

異步GET是指在另外的工作線程中執(zhí)行http請(qǐng)求, 請(qǐng)求時(shí)不會(huì)阻塞當(dāng)前的線程, 所以可以在Android主線程中使用.

onFailureonResponse的回調(diào)是在子線程中的,我們需要切換到主線程才能操作UI控件

val client = OkHttpClient()

fun run(url: String) {
    val request: Request = Request.Builder()
        .url(url)
        .build()
    val call =client.newCall(request)
    call.enqueue(object : Callback {
        override fun onResponse(call: Call, response: Response) {
            println("onResponse: ${response.body.toString()}")
        }

        override fun onFailure(call: Call, e: IOException) {
            println("onFailure: ${e.message}")
        }
    })
}

異步請(qǐng)求的步驟和同步請(qǐng)求類似昵慌,只是調(diào)用了Callenqueue方法異步請(qǐng)求假夺,結(jié)果通過回調(diào)CallbackonResponse方法及onFailure方法處理。

看了兩種不同的Get請(qǐng)求斋攀,基本流程都是先創(chuàng)建一個(gè)OkHttpClient對(duì)象已卷,然后通過Request.Builder()創(chuàng)建一個(gè)Request對(duì)象,OkHttpClient對(duì)象調(diào)用newCall()并傳入Request對(duì)象就能獲得一個(gè)Call對(duì)象淳蔼。

而同步和異步不同的地方在于execute()enqueue()方法的調(diào)用侧蘸,

調(diào)用execute()為同步請(qǐng)求并返回Response對(duì)象;

調(diào)用enqueue()方法測試通過callback的形式返回Response對(duì)象鹉梨。

注意:無論是同步還是異步請(qǐng)求讳癌,接收到Response對(duì)象時(shí)均在子線程中,onFailure存皂,onResponse的回調(diào)是在子線程中的,我們需要切換到主線程才能操作UI控件

POST請(qǐng)求

POST請(qǐng)求與GET請(qǐng)求不同的地方在于Request.Builderpost()方法晌坤,post()方法需要一個(gè)RequestBody的對(duì)象作為參數(shù)

同步POST請(qǐng)求

val body = new FormBody.Builder()
            .add(key,value)
            .build();

val request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

val response = client.newCall(request).execute();
val body =response.body().string()
println("post response: ${body}")

GET同步請(qǐng)求類似,只是創(chuàng)建Request時(shí)通過Request.Builder.post()方法設(shè)置請(qǐng)求類型為POST請(qǐng)求并設(shè)置了請(qǐng)求體。

異步表單提交

val body = FormBody.Builder()
            .add(key,value)
            .add(key1,value2)
            .build();

val request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

val call = client.newCall(request)
call.enqueue(new Callback(){
  @Override
   public void onFailure(Request request, IOException e){

   }

   @Override
   public void onResponse(final Response response) throws IOException{
       // 回調(diào)的結(jié)果是在子線程中的,我們需要切換到主線程才能操作UI控件 
       String response =  response.body().string();
   }
}

異步表單文件上傳

val file = File(Environment.getExternalStorageDirectory(), "1.png")
if (!file.exists()) {
    Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show()
         return
}
val muiltipartBody: RequestBody = MuiltipartBody.Builder() 
     .setType(MultipartBody.FORM)//一定要設(shè)置這句
     .addFormDataPart("username", "admin") //
     .addFormDataPart("password", "admin") //
     .addFormDataPart( "file", "1.png",RequestBody.create(MediaType.parse("application/octet-stream"), file))
      .build()

異步提交字符串

val mediaType = MediaType.parse("text/plain;charset=utf-8")
val body = "{username:admin, password:admin}"
RequestBody body = RequestBody.create(mediaType,body);

val request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

val call = client.newCall(request)
call.enqueue(new Callback(){
  @Override
   public void onFailure(Request request, IOException e){

   }

   @Override
   public void onResponse(final Response response) throws IOException{
       // 回調(diào)的結(jié)果是在子線程中的,我們需要切換到主線程才能操作UI控件 
       String response =  response.body().string();
   }
}

攔截器LoggingInterceptor

攔截器是OkHttp當(dāng)中一個(gè)比較強(qiáng)大的機(jī)制泡仗,可以監(jiān)視、重寫和重試調(diào)用請(qǐng)求猜憎。

這是一個(gè)比較簡單的Interceptor的實(shí)現(xiàn)娩怎,對(duì)請(qǐng)求的發(fā)送和響應(yīng)進(jìn)行了一些信息輸出。

// 添加攔截器
client.addInterceptor(LoggingInterceptor())
// 自定義日志打印攔截器
class LoggingInterceptor: Interceptor {
  @Override fun intercept(chain:Interceptor.Chain):Response  {
    val request = chain.request();
    val time_start = System.nanoTime();
    println(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));

    val response = chain.proceed(request);

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

    return response;
  }
}
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive

使用Gson來解析網(wǎng)絡(luò)請(qǐng)求響應(yīng)

Gson是Google開源的一個(gè)JSON庫胰柑,被廣泛應(yīng)用在Android開發(fā)中

app/build.gradle中添加以下依賴配置

dependencies {
  implementation 'com.google.code.gson:gson:2.8.6'
}
 class Account {
     var uid:String="00001;
     var userName:String="Freeman";
     var password:String="password";
     var telNumber:String="13000000000";
}

將JSON轉(zhuǎn)換為對(duì)象

val json ="{\"uid\":\"00001\",\"userName\":\"Freeman\",\"telNumber\":\"13000000000\"}";

val account = gson.fromJson(json, Account.class);
println(receiveAccount.toString());

將對(duì)象轉(zhuǎn)換為JSON

val gson = GSON()

val account = new Account()
println(gson.toJson(account));
輸出結(jié)果===> {"uid":"00001","userName":"Freeman","telNumber":"13000000000"}

將集合轉(zhuǎn)換成JSON

val gson = GSON()

val accountList = ArrayList<Account>();
accountList.add(account);

println(gson.toJson(accountList));
輸出結(jié)果===> [{"uid":"00001","userName":"Freeman","telNumber":"13000000000"}]

將JSON轉(zhuǎn)換成集合

val json= "[{\"uid\":\"00001\",\"userName\":\"Freeman\",\"telNumber\":\"13000000000\"}]"

val accountList = gson.fromJson(json, TypeToken<List<Account>>(){}.getType());
println("accountList size:${accountList.size()}");

JsonToKotlin 插件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末截亦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柬讨,更是在濱河造成了極大的恐慌崩瓤,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踩官,死亡現(xiàn)場離奇詭異却桶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蔗牡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門颖系,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辩越,你說我怎么就攤上這事嘁扼。” “怎么了黔攒?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵趁啸,是天一觀的道長。 經(jīng)常有香客問我督惰,道長不傅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任赏胚,我火速辦了婚禮蛤签,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘栅哀。我一直安慰自己震肮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布留拾。 她就那樣靜靜地躺著戳晌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痴柔。 梳的紋絲不亂的頭發(fā)上沦偎,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼豪嚎。 笑死搔驼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侈询。 我是一名探鬼主播舌涨,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扔字!你這毒婦竟也來了囊嘉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤革为,失蹤者是張志新(化名)和其女友劉穎扭粱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體震檩,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琢蛤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抛虏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虐块。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嘉蕾,靈堂內(nèi)的尸體忽然破棺而出贺奠,到底是詐尸還是另有隱情,我是刑警寧澤错忱,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布儡率,位于F島的核電站,受9級(jí)特大地震影響以清,放射性物質(zhì)發(fā)生泄漏儿普。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一掷倔、第九天 我趴在偏房一處隱蔽的房頂上張望眉孩。 院中可真熱鬧,春花似錦勒葱、人聲如沸浪汪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽死遭。三九已至,卻和暖如春凯旋,著一層夾襖步出監(jiān)牢的瞬間呀潭,已是汗流浹背钉迷。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钠署,地道東北人糠聪。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像谐鼎,于是被迫代替她去往敵國和親舰蟆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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