天天看點

OkHttp3-Android網絡請求架構常用用法介紹與執行個體(mob請求天氣預報)

轉載請标明出處:http://blog.csdn.net/donkor_/article/details/53589316

前言:

OkHttp是Square開發的第三方庫,用于發送和接收基于HTTP的網絡請求。它建立在Okio庫之上,通過建立共享記憶體池,它嘗試通過标準Java I / O庫更高效地讀取和寫入資料。它還是Retrofit庫的底層庫,為使用基于REST的API提供類型安全性。Square公司是不是看着很 眼紅 ( 眼熟 ),是的沒錯,這家公司在開源的道路上做足了貢獻,造福了無數程式員。除了OkHttp外,還有Picasso、Retrofit、otto等著名的開源項目。目前OkHttp最新版本是3.x,支援Android 2.3+,是以以下所講内容都是基于OkHttp3.x。

OkHttp項目開源位址 :https://github.com/square/okhttp

基本使用:

●配置與導入

在AndroidManifest.xml檔案中打開了聯網的權限:

在Android Studio 中配置gradle:

●發送和接收網絡請求

執行個體化一個OkHttpClient并建立一個Request對象。

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
           

如果有任何需要添加的查詢參數,OkHttp提供的HttpUrl類可以用來構造URL:

HttpUrl.Builder urlBuilder = HttpUrl.parse("http://blog.csdn.net/donkor_").newBuilder();
//addQueryParameter 添加查詢參數
urlBuilder.addQueryParameter("name", "donkor");
urlBuilder.addQueryParameter("blog", "okhttp3");
urlBuilder.addQueryParameter("number", "8");
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
                     .url(url)
                     .build();
           
●同步Get

因為Android不允許主線程上的網絡調用,是以隻能在單獨的線程或背景服務上進行同步調用。

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder().url(url).build();
            //newCall方法會得到一個Call對象,表示一個新的網絡請求
            //execute方法是同步方法,會阻塞目前線程,并傳回Response對象
            okhttp3.Response response = client.newCall(request).execute();
            String data=response.body().string();
            if (response.isSuccessful()) {
                 Log.e("asd","okHttp is request success");
            } else {
                Log.e("asd", "okHttp is request error");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();
           

※ 注:通過Response對象的body()方法可以得到響應體ResponseBody對象,調用其string()方法可以很友善地将響應體中的資料轉換為字元串,該方法會将所有的資料放入到記憶體之中,是以如果資料超過1M,最好不要調用string()方法以避免占用過多記憶體,這種情況下可以考慮将資料當做Stream流處理。

●異步Get
//enqueue方法調用異步請求網絡,該方法接收一個okhttp3.Callback對象,
// 且不會阻塞目前線程,會新開一個工作線程,讓實際的網絡請求在工作線程中執行。
//當異步請求成功後,會回調Callback對象的onResponse方法,在該方法中可以擷取Response對象。
// 當異步請求失敗或者調用了Call對象的cancel方法時,會回調Callback對象的onFailure方法。
// onResponse和onFailure這兩個方法都是在工作線程中執行的。
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.e("asd", "okHttp is request erro");
        Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        Log.e("asd", "okHttp is request success");
        String data=response.body().string();
        //在主線程中進行UI修改操作
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
            //do something
            }
        });
    }
});
           
●Post方式發送String

使用HTTP POST送出請求到服務。這個例子送出了一個markdown文檔到web服務,以HTML方式渲染markdown。因為請求體會放置在記憶體中,是以應該避免用該API發送超過1M的資料。

public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";
    //post方法接收一個RequestBody對象
    //create方法第一個參數都是MediaType類型,create方法的第二個參數可以是String、File、byte[]或okio.ByteString
    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}
           
●POST方式發送Stream流

這裡我們将請求主體作為流。 請求體的内容由流寫入産生。 此示例直接流入Okio緩沖接收器。 您的程式可能更喜歡OutputStream,您可以從BufferedSink.outputStream()擷取。

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
  //重寫contentType()方法,傳回markdown類型的MediaType
  @Override public MediaType contentType() {
    return MEDIA_TYPE_MARKDOWN;
  }

  //重寫writeTo()方法,該方法會傳入一個Okia的BufferedSink類型的對象,
  //可以通過BufferedSink的各種write方法向其寫入各種類型的資料,
  //此例中用其writeUtf8方法向其中寫入UTF-8的文本資料。
  //也可以通過它的outputStream()方法,得到輸出流OutputStream,
  //進而通過OutputSteram向BufferedSink寫入資料。
  @Override public void writeTo(BufferedSink sink) throws IOException {
    sink.writeUtf8("Numbers\n");
    sink.writeUtf8("-------\n");
    for (int i = ; i <= ; i++) {
      sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
    }
  }

  private String factor(int n) {
    for (int i = ; 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();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());
}
           
●POST方式發送檔案File

上傳檔案在實際開發中也經常用到,這裡比較簡單,直接看代碼:

public static final MediaType MEDIA_TYPE_MARKDOWN
    = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
  //File("/你的檔案路徑/名稱")
  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();

  Response response = client.newCall(request).execute();
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

  System.out.println(response.body().string());
}
           
●POST方式Form表單中的鍵值對

使用FormBody.Builder來建構類似于HTML 标簽的請求體。鍵值對将使用一種HTML相容形式的URL編碼來進行編碼。

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
  RequestBody formBody = new FormBody.Builder()
      .add("search", "Jurassic Park")
      .build();
  Request request = new Request.Builder()
      .url("https://en.wikipedia.org/w/index.php")
      .post(formBody)
      .build();

  Response response = client.newCall(request).execute();
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

  System.out.println(response.body().string());
}
           
●響應緩存

要想緩存響應,就需要配置一個可以讀寫的緩存目錄,以及緩存大小的限制。 并且緩存目錄應該是私有的,不受信任的應用程式不應該能夠讀取其内容!

一個緩存目錄同時被多個緩存通路是錯誤的。 大多數應用程式應該隻需調用一次OkHttpClient(),并配置它的緩存,之後隻需要調用這個執行個體即可。 否則,兩個緩存執行個體會互相幹擾,破壞響應緩存,并可能導緻應用程式崩潰。

private final OkHttpClient client;

public CacheResponse(File cacheDirectory) throws Exception {
  //設定緩存上限為M
  int cacheSize =  *  * ; 
  Cache cache = new Cache(cacheDirectory, cacheSize);

  //new OkHttpClient隻執行個體化一次,避免多個緩存執行個體互相幹擾
  client = new OkHttpClient.Builder()
      .cache(cache)
      .build();
}

public void run() throws Exception {
  Request request = new Request.Builder()
      .url("http://publicobject.com/helloworld.txt")
      .build();

  Response response1 = client.newCall(request).execute();
  if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);

  String response1Body = response1.body().string();
  System.out.println("Response 1 response:          " + response1);
  //對于同一個url位址,第一次獲得緩存資料 為null
  System.out.println("Response 1 cache response:    " + response1.cacheResponse());
  //對于同一個url位址,第一次獲得請求資料 不為null
  System.out.println("Response 1 network response:  " + response1.networkResponse());

  Response response2 = client.newCall(request).execute();
  if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);

  String response2Body = response2.body().string();
  System.out.println("Response 2 response:          " + response2);
  //對于同一個url位址,第二次獲得緩存資料 不為null
  System.out.println("Response 2 cache response:    " + response2.cacheResponse());
  //對于同一個url位址,第二次獲得請求資料 為null
  System.out.println("Response 2 network response:  " + response2.networkResponse());

  System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
           

※ 注:如果想讓某次請求禁用緩存,可以調用request.cacheControl(CacheControl.FORCE_NETWORK)方法,這樣即便緩存目錄有對應的緩存,也會忽略緩存,強制發送網絡請求,這對于擷取最新的響應結果很有用。如果想強制某次請求使用緩存的結果,可以調用request.cacheControl(CacheControl.FORCE_CACHE),這樣不會發送實際的網絡請求,而是讀取緩存,即便緩存資料過期了,也會強制使用該緩存作為響應資料,如果緩存不存在,那麼就傳回504 Unsatisfiable Request錯誤。

●取消請求Call

當請求不再需要的時候,我們應該中止請求,比如退出目前的Activity了,那麼在Activity中發出的請求應該被中止。可以通過調用Call的cancel方法立即中止請求,如果線程正在寫入Request或讀取Response,那麼會抛出IOException異常。使用這個api可以節約網絡資源。同步請求和異步請求都可以被取消。

private final ScheduledExecutorService executor = Executors.newScheduledThreadPool();
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
  Request request = new Request.Builder()
      .url("http://httpbin.org/delay/2") // This URL is served with a  second delay.伺服器端會有兩秒的延時
      .build();

  final long startNanos = System.nanoTime();
  final Call call = client.newCall(request);

  // Schedule a job to cancel the call in  second.
  //用戶端送出請求秒之後,請求還未完成,這時候通過cancel方法中止了Call,請求中斷,并觸發IOException異常
  executor.schedule(new Runnable() {
    @Override public void run() {
      System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / f);
      call.cancel();
      System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / f);
    }
  }, , TimeUnit.SECONDS);

  try {
    System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / f);
    Response response = call.execute();
    System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
        (System.nanoTime() - startNanos) / f, response);
  } catch (IOException e) {
    System.out.printf("%.2f Call failed as expected: %s%n",
        (System.nanoTime() - startNanos) / f, e);
  }
}
           
●設定逾時

沒有響應時使用逾時結束call。沒有響應的原因可能是客戶點連結問題、伺服器可用性問題或者這之間的其他東西。OkHttp支援連接配接,讀取和寫入逾時。

private final OkHttpClient client;

  public ConfigureTimeouts() throws Exception {
    //connectTimeout方法設定用戶端和伺服器建立連接配接的逾時時間
    //writeTimeout方法設定用戶端上傳資料到伺服器的逾時時間
    //readTimeout方法設定用戶端從伺服器下載下傳響應資料的逾時時間
    client = new OkHttpClient.Builder()
        .connectTimeout(, TimeUnit.SECONDS)
        .writeTimeout(, TimeUnit.SECONDS)
        .readTimeout(, TimeUnit.SECONDS)
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
        .build();

    Response response = client.newCall(request).execute();
    System.out.println("Response completed: " + response);
  }
           

執行個體:Gson處理複雜JSON字元串,并擷取天氣情況:

1.在使用免費的天氣預報api,這裡用的是mob平台提供的,有需要的直接在下面連結注冊使用,這裡不再過多贅述。

http://api.mob.com/#/apiwiki/weather

2.mob平台擷取天氣狀況請求方式是get,這裡我們使用同步與異步get分别進行請求

3.請求成功,傳回的資料是json格式,是以我們使用Gson進行解析,并顯示在文本上

4.配置gradle與請求網絡權限

compile files('libs/gson-2.8.0.jar')
    compile files('libs/okhttp-3.5.0.jar')
    compile files('libs/okio-1.11.0.jar')
           

5.添加請求天氣預報的url位址,這裡我們以深圳為例

//url為請求位址  key=19d6b7c760314  key為注冊應用成功後獲得
    private final String url = "http://apicloud.mob.com/v1/weather/query?key=19d6b7c760314&city=深圳";
           

6.初始化Gson與OkHttpClient

private Gson gson = new Gson();
    private OkHttpClient client= new OkHttpClient();
           

7.根據json字元串的複雜程式,定義需要序列化的bean。下圖是我擷取得到天氣預報并列印成字元串的截圖

OkHttp3-Android網絡請求架構常用用法介紹與執行個體(mob請求天氣預報)

8.根據上圖擷取的json字元串,要得到future最近四天的天氣,這裡我們定義三個類。

CommWeather.java

public class CommWeather {

    private String retCode;
    private List<Results> result;


    public String getRetCode() {
        return retCode;
    }

    public void setRetCode(String retCode) {
        this.retCode = retCode;
    }

    public List<Results> getResult() {
        return result;
    }

    public void setResult(List<Results> result) {
        this.result = result;
    }
}
           

Results.java

public class Results {

    private String city;
    private String sunrise;
    private String sunset;
    private List<Future> future;

    public List<Future> getFuture() {
        return future;
    }

    public void setFuture(List<Future> future) {
        this.future = future;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getSunrise() {
        return sunrise;
    }

    public void setSunrise(String sunrise) {
        this.sunrise = sunrise;
    }

    public String getSunset() {
        return sunset;
    }

    public void setSunset(String sunset) {
        this.sunset = sunset;
    }
}
           

Future.java

public class Future {
    private String date;
    private String dayTime;
    private String night;
    private String temperature;
    private String wind;

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getDayTime() {
        return dayTime;
    }

    public void setDayTime(String dayTime) {
        this.dayTime = dayTime;
    }

    public String getNight() {
        return night;
    }

    public void setNight(String night) {
        this.night = night;
    }

    public String getTemperature() {
        return temperature;
    }

    public void setTemperature(String temperature) {
        this.temperature = temperature;
    }

    public String getWind() {
        return wind;
    }

    public void setWind(String wind) {
        this.wind = wind;
    }

}
           

9.使用okhttp同步/異步get擷取得到天氣預報的json資料,并進行解析,并顯示在UI上。這裡我們看下MainActivity中異步操作并解析的主要代碼:

private void getWeatherAsync() {
        //CacheControl.FORCE_NETWORK 不進行緩存
        Request request = new Request.Builder().url(url).cacheControl(CacheControl.FORCE_NETWORK).build();

        //enqueue方法調用異步請求網絡,該方法接收一個okhttp3.Callback對象,
        // 且不會阻塞目前線程,會新開一個工作線程,讓實際的網絡請求在工作線程中執行。
        //當異步請求成功後,會回調Callback對象的onResponse方法,在該方法中可以擷取Response對象。
        // 當異步請求失敗或者調用了Call對象的cancel方法時,會回調Callback對象的onFailure方法。
        // onResponse和onFailure這兩個方法都是在工作線程中執行的。
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e("asd", "okHttp is request error");
                Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("asd", "okHttp is request success");
                //擷取伺服器傳回的json字元串
                String responseString = response.body().string();
                Log.e("asd", "responseString: " + responseString);
                //使用Gson解析json字元串
                CommWeather commWeather = gson.fromJson(responseString, CommWeather.class);

                //retCode==200 請求成功
                if (commWeather.getRetCode().equals("200")) {
                    List<Results> listResult = commWeather.getResult();
                    //根據mob的api文檔可以确定,result始終size為1
                    for (int i = ; i < listResult.size(); i++) {
                        final String city = listResult.get(i).getCity();
                        final String sunrise = listResult.get(i).getSunrise();
                        final String sunset = listResult.get(i).getSunset();
                        //在主線程中修改UI
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                tvCity.setTextColor(android.graphics.Color.RED);
                                tvCity.setText("Aysnc異步獲得的"+city + " 的日出時間: " + sunrise + " 和日落時間" + sunset);
                            }
                        });

                        //這裡我們隻需要擷取最近四天的天氣
                        final List<Future> listFuture = listResult.get(i).getFuture();
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        //修改UI操作
                                    }
                                });
                    }
                }
            }
        });
    }
           

最後看下效果圖

OkHttp3-Android網絡請求架構常用用法介紹與執行個體(mob請求天氣預報)

CSDN下載下傳(内含最新jar包): http://download.csdn.net/detail/donkor_/9710663

About me

Email :[email protected]

Android開發交流QQ群 : 537891203

OkHttp3-Android網絡請求架構常用用法介紹與執行個體(mob請求天氣預報)