OKGO添加upRequestBody json_#okhttp-緩存

前言:OkHttp從零開始學習,首先是來自OkHttpClient文檔註釋的簡單翻譯,簡單瞭解下注意事項和用法

1、 Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their
responses.

想要發送和接收http請求,需要call這個類

2、OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for
all of your HTTP calls. This is because each client holds its own connection pool and thread
pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
client for each request wastes resources on idle pools.

okhttp的最好方式是創建一個單例模式,並且重複使用它來進行http請求。因為每一個http客户端擁有自己連接池和線程池。重複利用連接池和線程池可以減少延遲和內存。

3 Use {@code new OkHttpClient()} to create a shared instance with the default settings:
*

{@code 

 * 

 *   // The singleton HTTP client. 

 *   public final OkHttpClient client = new OkHttpClient(); 

 * }



*


*

Or use {@code new OkHttpClient.Builder()} to create a shared instance with custom settings:
*

{@code 

 * 

 *   // The singleton HTTP client. 

 *   public final OkHttpClient client = new OkHttpClient.Builder() 

 *       .addInterceptor(new HttpLoggingInterceptor()) 

 *       .cache(new Cache(cacheDir, cacheSize)) 

 *       .build(); 

 * }

創建一個http客户端實例,可以使用默認配置也可以自定義配置,比如緩存和攔截器

4、 This example shows a call with a short 500 millisecond timeout:

{@code 

 * 

 *   OkHttpClient eagerClient = client.newBuilder() 

 *       .readTimeout(500, TimeUnit.MILLISECONDS) 

 *       .build(); 

 *   Response response = eagerClient.newCall(request).execute(); 

 * }

一個簡單的http訪問的例子,設置了讀取失效時間。

通過簡單的閲讀註釋,可以發現OkHttpClient有線程池和連接池,最好使用單例模式,所以我們肯定要對其進行封裝,不要像以前apach的HttpClient一樣,每一個請求都new一個client對象,而且我們可以配置自己響應超時時間、緩存、攔截器等等配置。

好了,從零開始進行一下功能的實現:

  1. 單例一個OkHttpClient
  2. 簡單的Get請求
  3. 簡單的Post請求
  4. 緩存實現
  5. seesion保持
  6. 文件上傳引入 compile ‘com.squareup.okhttp3:okhttp:3.4.2’一、實現一個單例
1. public class OkHttpUtil { 
 //讀超時長,單位:毫秒 
 public static final int READ_TIME_OUT = 7676; 
 //連接時長,單位:毫秒 
 public static final int CONNECT_TIME_OUT = 7676;
private static OkHttpUtil okHttpUtil; 
 private static OkHttpClient okHttpClient;
private OkHttpUtil() { 
 okHttpClient = new OkHttpClient(); 
 } 
 public static OkHttpUtil getInstance() { 
 if (null == okHttpUtil) { 
 synchronized (OkHttpUtil.class) { 
 if (null == okHttpUtil) { 
 okHttpUtil = new OkHttpUtil(); 
 } 
 } 
 } 
 return okHttpUtil; 
 }
public void doGet(String url) {
} 
 }

二、get請求,傳入url,返回call,調用者拿到call可以進去取消:call.cancel();

public Call doGetBackCall(String url,  Callback callback) {
    Request request = new Request.Builder().url(url).build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);
    return call;
}

三、post請求,以表單的形式

public Call doPostBackCall(HashMap<String, String> params, String url,  Callback callback) {
    FormBody.Builder formBodybuilder = new FormBody.Builder();
    Iterator iter = params.entrySet().iterator();
    while (iter.hasNext()) {
        Map.Entry<String, String> entry = (Map.Entry<String, String>) iter.next();
        String key = entry.getKey();
        String val = entry.getValue();
        formBodybuilder.add(key, val);
    }
    RequestBody requestBody = formBodybuilder.build();
    Request request = new Request.Builder().url(url).post(requestBody).build();
    Call call = okHttpClient.newCall(request);

    call.enqueue(callback);
    return call;
}

調用:

public void doPost(){
    HashMap<String,String> map= new HashMap<>();
    map.put("username","lmj");
    map.put("pwd","1234556");
    try {
        OkHttpUtil.getInstance().doPostBackCall(map,"http://192.168.3.171:8080/LmjWeb2/test", new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.i("aaa", "加載失敗:");
                runOnUiThread(
                        new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this, "加載失敗:",Toast.LENGTH_SHORT).show();
                            }
                        }
                );
            }
            @Override
            public void onResponse(Call call,  Response response) throws IOException {
                final String str = response.body().string();
                Log.i("aaa", "加載成功:" + str);
                runOnUiThread(
                        new Runnable() {
                            @Override
                            public void run() {

                                Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();
                            }
                        }
                );
            }
        });

    } catch (Exception e) {
        e.printStackTrace();
    }
}

四、緩存的實現,我現在測試出來的是有網絡進去網絡請求,不管請求失敗還是成功都不會使用緩存,在沒有網絡的時候根據需要判斷要不要使用緩存,並且這個緩存時間我還控制不了。。。求大神

(1)首先是實現一個攔截器

private static final long CACHE_STALE_SEC = 60 * 1;
private static final Interceptor mRewriteCacheControlInterceptor = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String cacheControl = request.cacheControl().toString();//根據請求的header,來判斷要不要使用緩存,retrofit可
        //以輕鬆實現在請求上加header
        if (!NetWorkUtils.isNetConnected(BaseApplication.getAppContext())) {
            request = request.newBuilder()
                    .cacheControl(TextUtils.isEmpty(cacheControl) ? CacheControl.FORCE_NETWORK : CacheControl.FORCE_CACHE)
                    .build();
        }
        Response originalResponse = chain.proceed(request);
        if (NetWorkUtils.isNetConnected(BaseApplication.getAppContext())) {
            //有網的時候讀接口上的@Headers裏的配置,你可以在這裏進行統一的設置
            return originalResponse.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        } else {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + CACHE_STALE_SEC)
                    .removeHeader("Pragma")
                    .build();
        }
    }
};

(2)配置我們的OkHttpClient客户端

cacheFile = new File(BaseApplication.getAppContext().getCacheDir(), "cache");
    cache = new Cache(cacheFile, 1024 * 1024 * 10); //10Mb
 okHttpClient = new OkHttpClient.Builder()
            .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
            .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
            .addInterceptor(mRewriteCacheControlInterceptor)
            .addNetworkInterceptor(mRewriteCacheControlInterceptor)
            .cache(cache)
            .build();

(3)改造一下get請求,添加設置緩存的header

public Call doGetBackCall(String url,int cacheTime , final Callback callback) {
    Request request = new Request.Builder().url(url)
    .addHeader("Cache-Control",cacheTime<1?"":"max-stale="+cacheTime).build();

    Call call = okHttpClient.newCall(request);
    call.enqueue(callback);

    return call;
}

五、sesson的保持:在很多時候需要客户端本地保存session值,然後在每個接口上面發送給服務器,所以我們要在返回中拿到sesseion,保存session,在每個請求頭中添加session。這裏我們只需要實現攔截器就可以了,然後給單例的客户端配置好這個攔截器。

private String sessionID = "";
private Interceptor receivedCookiesInterceptor = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());

        if (!originalResponse.headers("Set-Cookie").isEmpty()) {
            HashSet<String> cookies = new HashSet<>();

            for (String header : originalResponse.headers("Set-Cookie")) {
                Log.i("aaa", "session:" + header);
                sessionID = header;
                cookies.add(header);
            }
        }

        return originalResponse;
    }
};
private Interceptor addCookiesInterceptor = new Interceptor() {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder builder = chain.request().newBuilder();
        builder.addHeader("Cookie", sessionID);

        return chain.proceed(builder.build());
    }
};
--給原來的client添加攔截器
okHttpClient = new OkHttpClient.Builder()
            .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
            .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
            .addInterceptor(addCookiesInterceptor)
            .addInterceptor(receivedCookiesInterceptor)
            .addInterceptor(mRewriteCacheControlInterceptor)
            .addNetworkInterceptor(mRewriteCacheControlInterceptor)
            .cache(cache)
            .build();

六、對於http文件上傳,其實就是post請求了,只是請求body的問題,在請求的時候可以在body裏面放置表單、json數據、file文件等等。直接粘貼hongyang大神代碼了,因為這個屬於http協議部分了。。。

File file = new File(Environment.getExternalStorageDirectory(), "balabala.mp4");

RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);

RequestBody requestBody = new MultipartBuilder()
 .type(MultipartBuilder.FORM)
 .addPart(Headers.of(
      "Content-Disposition", 
          "form-data; name=\"username\""), 
      RequestBody.create(null, "張鴻洋"))
 .addPart(Headers.of(
     "Content-Disposition", 
     "form-data; name=\"mFile\"; 
     filename=\"wjd.mp4\""), fileBody)
 .build();

Request request = new Request.Builder()
.url("http://192.168.1.103:8080/okHttpServer/fileUpload")
.post(requestBody)
.build();

Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback()
{
//...
});

注意攔截器
應用攔截器

不需要關心像重定向和重試這樣的中間響應。
總是調用一次,即使HTTP響應從緩存中獲取服務。
監視應用原始意圖。不關心OkHttp注入的像If-None-Match頭。
允許短路並不調用Chain.proceed()。
允許重試並執行多個Chain.proceed()調用。
網絡攔截器

可以操作像重定向和重試這樣的中間響應。
對於短路網絡的緩存響應不會調用。
監視即將要通過網絡傳輸的數據。
訪問運輸請求的Connection。

在網絡請求結果response在攔截器被讀取,會導致回調時候流被關閉了讀取不到數據
只有用通過peekBody()或者

MediaType contentType = null; 
 String bodyString = null; 
 if (originalResponse.body() != null) { 
 contentType = originalResponse.body().contentType(); 
 bodyString = originalResponse.body().string(); 
 Log.i(“AAA”,bodyString); 
 } 
 okhttp3.ResponseBody body = okhttp3.ResponseBody.create(contentType, bodyString); 
 return originalResponse.newBuilder().body(body).build();