从零开始学习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’

    一、实现一个单例

    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();

源码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值