OKHTTP

一.为什么要用okhttp

支持HTTP2/SPDY,允许所有同一个主机地址的请求共享同一个Socket连接,减少了三次握手和四次挥手的过程,效率更高

二.一些类的简单介绍

okHttpClient:构建者模式,可以设置拦截器、分发器等参数。
Request:封装的网络请求,请求实体类
Call:exectute()方法执行RealCall的execute方法client.dispatcher().enqueue(new AsyncCall(responseCallback));利用dispatcher调度器enqueueAsyncCall,并通过回调(Callback)获取服务器返回的结果
Dispather:调度器,持有线程池,和三个任务队列,拿到请求封装类,去执行call,拿到执行结果Respose。
Respose:respose.body().byteStream(),拿到分发器中的请求结果。
CallBack:将请求的结果回调给调用者。

三.基本使用

public class OkHttpUtils {
    private final static String url = "https://www.httpbin.org/";
    private static OkHttpUtils instance;

    private OkHttpClient okHttpClient = new OkHttpClient();

    private OkHttpUtils() {
    }

    public static OkHttpUtils getInstance() {
        if (null == instance) {
            synchronized (OkHttpUtils.class) {
                if (null == instance) {
                    instance = new OkHttpUtils();
                }
            }
        }
        return instance;
    }

    //同步get请求
    public void synGet() {


        Request request = new Request.Builder().url(url + "get?username=123&password=123").build();
        try {
            Response response = okHttpClient.newCall(request)
                    .execute();
            System.out.println("threadName:" + Thread.currentThread().getName());
            System.out.println(Objects.requireNonNull(response.body().string()));
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        }
    }
 //异步get请求
    public void aSynGet() {

        Request request = new Request.Builder().url(url + "get?username=123&password=123").get().build();
        startCall(request);
    }
 // form表单 post请求 contentType application/x-www-form-urlencoded,multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
    public void postForm() {
        FormBody formBody = new FormBody.Builder().add("use", "abc")
                .add("pwd", "123").build();
        Request request = new Request.Builder().url(url + "post").post(formBody).build();
        startCall(request);
    }

    //post请求上传二进制文件,只上传单个文件  contentType可查看菜鸟教程https://www.runoob.com/http/http-content-type.html
    public void postOctStream() {
        try {
            RequestBody streamBody = RequestBody.create(MediaType.parse("application/octet-stream"), "123".getBytes("utf-8"));
            UploadRequestBody uploadRequestBody = new UploadRequestBody(new UploadRequestBody.ProgressListener() {
                @Override
                public void progress(String progress, long contentLength, long currentWrite) {
                    System.out.println("progress:" + progress + "contentLength:" + contentLength + ",currentWrite:" + currentWrite);
                }
            }, streamBody);
            Request request = new Request.Builder().url(url + "post").post(uploadRequestBody).build();
            startCall(request);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    //post多类型上传
    public void postMultiPart() {
        try {
            MultipartBody multipartBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("username", "abc")
                    .addFormDataPart("pwd", "123")
                    .addFormDataPart("file", "fileName",
                            RequestBody.create(MediaType.parse("application/octet-stream"), "123".getBytes("utf-8"))).build();
            Request request = new Request.Builder().url(url + "post").post(multipartBody).build();
            startCall(request);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    //下载
    public void downloadImage(ImageView imageView) {

        Request request = new Request.Builder().get().url("https://img0.baidu.com/it/u=1501084209,93021381&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500")
                .build();
        startCall(request, imageView);


    }

    /**
     * 普通拦截器和网络拦截器
     *
     */
    public void addInterceptor() {
        okHttpClient.newBuilder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept( Chain chain) throws IOException {
                        //前置处理 添加请求头等等
                        Request request = chain.request().newBuilder()
                                .addHeader("os", "android")
                                .addHeader("version", "1.o")
                                .build();

                        Response response = chain.proceed(request);
                        //后置处理
                        return chain.proceed(request);
                    }
                }).addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept( Chain chain) throws IOException {

                System.out.println("version>>>>>" + chain.request().header("version"));
                return chain.proceed(chain.request());
            }
        }).build();
    }

    /**
     * 默认情况下okHttp的缓存是关闭的,需要我们手动开启的
     *
     * http://mushuichuan.com/2016/03/01/okhttpcache/
     */
    public void addCache() {
        okHttpClient.newBuilder()
                .cache(new Cache(new File("path/cache"), 100))
                .build();
    }

    public void addCookieJar() {
        final Map> cookiesMap = new HashMap<>();
        okHttpClient.newBuilder()
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse( HttpUrl httpUrl,  List list) {
                        //返回服务器的cookie 存到本地
                        cookiesMap.put(httpUrl.host(), list);
                    }

                    @Override
                    public List loadForRequest( HttpUrl httpUrl) {
                        List cookies = cookiesMap.get(httpUrl.host());
                        return cookies == null ? new ArrayList() : cookies;
                    }
                })
                .build();
    }

    private void startCall(Request request) {
        startCall(request, null);
    }

    //开始请求
    private void startCall(final Request request, final ImageView imageView) {
        try {
            okHttpClient.newCall(request)
                    .enqueue(new Callback() {
                        @Override
                        public void onFailure( Call call,  IOException e) {
                            System.out.println("onFailure:" + e.getMessage());
                        }

                        @Override
                        public void onResponse( Call call,  Response response) throws IOException {
                            System.out.println("threadName:" + Thread.currentThread().getName());

                            if (null != imageView) {
                                InputStream inputStream = response.body().byteStream();
                                File file = new File(imageView.getContext().getFilesDir().getPath() + File.separator + "a.png");
                                if (!file.exists()) {
                                    System.out.println("file:" + file.getPath());
                                    file.createNewFile();
                                }
                                FileOutputStream fos = new FileOutputStream(file);
                                float sum = 0l;
                                long total = response.body().contentLength();
                                byte buff[] = new byte[128];
                                int length;
                                while ((length = inputStream.read(buff)) != -1) {
                                    fos.write(buff, 0, length);
                                    sum += length;
                                    float speed = sum / total * 100;
                                    String format = (new DecimalFormat("0.00")).format(speed);
                                    String format1 = String.format("%.2f", speed);
                                    System.out.println("speed:" + speed + ",sum:" + sum + ",total:" + total + ",format:" + format + ",format1:" + format1);

                                }
                                fos.flush();
                                final Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                                imageView.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        imageView.setImageBitmap(bitmap);
                                    }
                                });
                                inputStream.close();
                            } else {
                                /**
                                 * 如果先body的string方法后调用byteStream流已经被关闭了,会导致io异常
                                 */
                                System.out.println(Objects.requireNonNull(response.body().string()));

                            }

                        }
                    });

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

    //上传进度
    private static class UploadRequestBody extends RequestBody {

        private ProgressListener progressListener;
        private RequestBody requestBody;

        public UploadRequestBody(ProgressListener progressListener, RequestBody requestBody) {
            this.progressListener = progressListener;
            this.requestBody = requestBody;
        }


        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        @Override
        public void writeTo( BufferedSink bufferedSink) throws IOException {
            System.out.println(">>>>>");
            CountSink countSink = new CountSink(bufferedSink);
            BufferedSink buffer = Okio.buffer(countSink);
            requestBody.writeTo(buffer);
            buffer.flush();


        }

        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        class CountSink extends ForwardingSink {
            long byteWrite;

            public CountSink(Sink sink) {
                super(sink);
            }


            /**
             * @param source
             * @param byteCount 缓冲区大小
             * @throws IOException
             */
            @Override
            public void write( Buffer source, long byteCount) throws IOException {
                super.write(source, byteCount);
                System.out.println("write>>>>>");
                byteWrite += byteCount;
                float speed = byteWrite / contentLength() * 100;
                String format = (new DecimalFormat("0.00")).format(speed);
                progressListener.progress(format, contentLength(), byteWrite);
            }


        }

        public interface ProgressListener {
            void progress(String progress, long contentLength, long currentWrite);
        }
    }

四.请求流程的简单分析

1.Dispather中异步执行和同步执行

异步执行:根据当前call请求数量是否大于64和同一个host请求是否大于5来决定加入到异步等待队列还是执行队列

  
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
            //线程池执行call
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
  }

同步执行:直接将call加入到同步运行队列

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

2.Call的执行过程

Call是个抽象类,它的请求在实现类RealCall中执行,异步请求执行,将请求包装成AsyncCall,并添加到分发器的请求队列中,AsyncCall是RealCall的内部类,它继承自NameRunnable,当线程池执行这个AsyncCall时,会执行他的run方法,而它的run方法就是调用AsyncCall的execute方法,请求执行完后,调用dispatcher分发器的移除运行队列中的这个call。同步执行则是直接把他加入到运行队列中,并阻塞当前线程,直到有结果或异常。

RealCall.java
  
    //同步执行
  @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
           //加入到runningSyncCalls队列
          client.dispatcher().executed(this);
            //获取返回结果
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
           //移除同步执行队列
          client.dispatcher().finished(this);
        }
  }
    
@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
   //加入到分发器的队列中
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
Dispatcher.java
 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
       //执行call
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

//异步执行完成
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
//同步执行完成
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private  void finished(Deque calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }
//AsyncCall是RealCall的内部类
final class AsyncCall extends NamedRunnable {
  
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
NamedRunnable.java
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}


  /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }


3.通过拦截器获取请求结果的过程

从上面的call执行过程我们可以看到,它是由分发器通过请求队列和线程池来集中处理的,无论是同步还是异步,请求的结果通过 Response response = getResponseWithInterceptorChain();这个方法返回,那我们就对这个方法进行简单的分析。

  Response getResponseWithInterceptorChain() throws IOException {
 
    List interceptors = new ArrayList<>();
      //你在new一个okHttpClient加入进去的拦截器,比如日志拦截器等
    interceptors.addAll(client.interceptors());
      // 在配置 OkHttpClient 时设置的 interceptors;
    interceptors.add(retryAndFollowUpInterceptor);
      // 请求时,对必要的Header进行一些添加,接收响应时,移除必要的Header
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    // 负责读取缓存直接返回、更新缓存
    interceptors.add(new CacheInterceptor(client.internalCache()));
      // 负责和服务器建立连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        //网络拦截器
      interceptors.addAll(client.networkInterceptors()); 
    }
      // 负责向服务器发送请求数据、从服务器读取响应数据
    interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

最后用一张图来对执行过程做个总结

okHttp.png

版权声明:
作者:siwei
链接:https://www.techfm.club/p/43461.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>