Android 開發 框架系列 OkHttp使用詳解


簡介

okhttp是一個第三方類庫,用於android中請求網絡。這是一個開源項目,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻(該公司還貢獻了Picasso和LeakCanary) 。用於替代HttpUrlConnection和Apache HttpClient(android API23 里已移除HttpClient)。

依賴

implementation 'com.squareup.okhttp3:okhttp:4.4.0'

同步請求方式(get請求)

步驟一  創建請求接口網址

我使用了http://www.sosoapi.com/ 來創建了一個訪問接口,用來驗證OkHttp是否請求成功。如果你有興趣了解,可以直接進入網站,里面有詳細的demo演示。后續我將不在贅述這段。

我在請求響應里添加了一段JSON數據:

[
    {
        "name": "get測試",
        "content": "你成功獲取了數據"
    }
]

步驟二  創建JSON解析方法

這里我寫一下解析JSON數據的方法,來解析get或者post得到的JSON數據。后續我將不在贅述這段。

/**
     * JSON 解析方法
     * @param jsonData
     * @return
     */
    public String readJSONContent(String jsonData){
        try {
            StringBuffer sb = new StringBuffer();
            JSONArray jsonArray = new JSONArray(jsonData);
            for (int i=0;i<jsonArray.length();i++){
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                sb.append(jsonObject.getString("name")+"\n");
                sb.append(jsonObject.getString("content")+"\n");
            }
            return sb.toString();
        } catch (JSONException e) {
            Log.e("JSONException錯誤", "readContent: "+e.toString());
            return e.toString();
        }
    }

步驟三  創建OkHttp 同步請求

/**
     * 同步請求
     */
    public void synchro(){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient okHttpClient = new OkHttpClient();//創建單例
                Request request = new Request.Builder()//創建請求
                        .url("http://www.sosoapi.com/pass/mock/12003/test/gettest")
                        .build();
                try {
                    Response response = okHttpClient.newCall(request).execute();//執行請求
                    mContent = response.body().string();//得到返回響應,注意response.body().string() 只能調用一次!

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mtextView.setText(readJSONContent(mContent));
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e("OkHttpActivity", e.toString() );
                }
            }
        });
        thread.start();
    }

注意!response.body().string() 只能調用一次! 因為響應主體 RessponseBody 持有的資源可能會很大,所以 OkHttp 並不會將其直接保存到內存中,只是持有數據流連接。只有當我們需要時,才會從服務器獲取數據並返回。同時,考慮到應用重復讀取數據的可能性很小,所以將其設計為一次性流(one-shot),讀取后即 '關閉並釋放資源'

 

效果圖:

 

異步請求方式(get請求)

注意1:異步請求方式,請求的回調會在子線程里,所以如果需要更新UI你需要切換到主線程。且你不需要在new 一個線程包裹這個異步請求了。另外如何切換到主線程請使用 Handler 例子:

注意2:在異步請求方法里,請不要將 public void onResponse(Call call, final Response response)回調里的response回調數據放到UI線程里解析,因為有一個天坑,有可能在UI線程里解析的時候response里面卻還沒有塞入數據(我也覺得很神奇,不知道寫okhttp的公司是怎么想的,為什么不處理完所有數據在提供回調)

 

private Handler mHandler = new Handler(Looper.getMainLooper());
            mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //主線程

                    }
                });    

創建異步請求

     /**
     * get異步請求
     */
      public void asynchronous(){
          OkHttpClient okHttpClient = new OkHttpClient();
          Request request = new Request.Builder()
                  .url("http://www.sosoapi.com/pass/mock/12003/test/gettest")
                  .build();
          okHttpClient.newCall(request).enqueue(new Callback() {
              @Override
              public void onFailure(Call call, IOException e) {
                  //失敗回調

              }

              @Override
              public void onResponse(Call call, final Response response) throws IOException {
            mContent = readJSONContent(response.body().string());//注意response.body().string() 只能調用一次!
//響應成功,這個回調在子線程中,所以不需要創建線程 if (response.isSuccessful()){ //isSuccessful方法:如果代碼位於(200…300)中,則返回true,這意味着請求已成功接收 runOnUiThread(new Runnable() { @Override public void run() { try { //因為在子線程,所以我們需要回到主線程中更新UI數據 mtextView.setText(mContent); } catch (IOException e) { e.printStackTrace(); } } }); } } }); }

Post請求

post請求也同時有同步與異步方法,與上面一致,所以這里就不展示了,下面我們來看看post請求發送

/**
     * post請求
     */
    public void post(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //實例
                OkHttpClient okHttpClient = new OkHttpClient();

                //創建post請求數據表單
                RequestBody requestBody = new FormBody.Builder()
                        .add("name","請求post")
                        .add("password","123456")
                        .build();
                //創建請求
                final Request request = new Request.Builder()
                        .url("http://www.sosoapi.com/pass/mock/12003/test/posttest")
                        .post(requestBody)//添加post請求
                        .build();

                try {
                    //發送請求得到響應
                    final Response response = okHttpClient.newCall(request).execute();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mtextView.setText(readJSONContent(response.body().string()));//注意response.body().string() 只能調用一次!
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }

效果圖:

取消網絡請求

取消okhttp的網絡請求很簡單只需要

call.cancel();

如何鑒別是網絡異常還是主動取消,請求取消的回調在onFailure(Call call, IOException e)里回調,這個時候我們需要再次判斷onFailure里回調的是我們自己主動取消的還是網絡異常報錯的

mCall.enqueue(new Callback() {//發送請求
    @Override
    public void onFailure(final Call call, final IOException e) {
        if (call.isCanceled()){
            listener.onCancel();

        }else {
            listener.onFailure(call, e);
        }
    }     
//省略下面代碼...

RequestBody創建

RequestBody是okhttp  post發送數據配置類.在這之前我們先了解下回顧一下body類型

 

http有四種body類型,Content-Type POST提交數據的方式:
  • application/x-www-form-urlencoded 表單數據
  • multipart/form-data 表單文件上傳
  • application/json 序列化JSON數據
  • text/xml XML數據

這些body類型需要在http header頭部就寫上,但是okhttp不需要我們手動在header寫上類型了.okhttp提供了FormBody和MultipartBody的類型,方便你的快速創建 application/x-www-form-urlencoded 與 multipart/form-data

FormBody 創建方式

/**
     * 此body是 默認application/x-www-form-urlencoded,你可以進入FormBody類查看,第一行靜態常量就是這個
     */
   public void FormBody(){
       FormBody.Builder builder = new FormBody.Builder();
       builder.add("key","content");
       builder.build();
   }

MultipartBody 創建方式

 

/**
     * multipart/form-data
     */
   public void MultipartBody(){
       RequestBody requestBody = new RequestBody() {
           @Override
           public MediaType contentType() {
               return null;
           }

           @Override
           public void writeTo(BufferedSink sink) throws IOException {
               //上傳流 sink.write()


           }
       };
       MultipartBody.Builder builder = new MultipartBody.Builder();
       builder.addFormDataPart("key","content");//表單數據
       builder.addFormDataPart("file_key","/path/image.jpg",requestBody);//文件數據
       MultipartBody multipartBody = builder.build();
   }

手動創建4種body

當然,有特殊需求你還可以添加手動其他body類型,create也支持好幾種數據的上傳形式

 /**
     * 手動創建4種body
     */
   public void RequestBody(){
       RequestBody applicationFormUrlencodedBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded; charset=utf-8"),new String("demo").getBytes());
       RequestBody multipartBody = RequestBody.create(MediaType.parse("multipart/form-data; charset=utf-8"),new File("/path/image.jpg"));
       RequestBody jsonBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),"content");
       RequestBody textbody = RequestBody.create(MediaType.parse("text/xml; charset=utf-8"),"content");

   }

 實現OkHttpClient客戶端單例模式

public class OkHttpClientCreate {
    private static final boolean IS_RETRY = false;//失敗是否重連
    private static final int CONNECT_TIME = 10;//設置連接超時時間 單位:秒
    private static final int READ_TIME = 10;//設置讀取超時時間
    private static final int WRITE_TIME = 10;//設置寫入超時間
    private static OkHttpClient mOkHttpClient;
    public static OkHttpClient CreateClient(){
        if (mOkHttpClient == null){
            return mOkHttpClient = new OkHttpClient.Builder()
                    .retryOnConnectionFailure(IS_RETRY)
                    .connectTimeout(CONNECT_TIME,TimeUnit.SECONDS)//連接超時
                    .readTimeout(READ_TIME,TimeUnit.SECONDS)//讀取超時
                    .writeTimeout(WRITE_TIME,TimeUnit.SECONDS)//寫入超時
//                    .callTimeout()//呼叫超時,設置此參數為整體流程請求的超時時間
//                    .addInterceptor() //設置攔截器
//                    .authenticator() //設置認證器
//                    .proxy()//設置代理
                    .build();
        }
        return mOkHttpClient;
    }

    public static void destroy(){
        mOkHttpClient = null;
    }
    
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM