可以说,okhttp非常的火,火了好几年了,虽然现如今比较火的网络框架是retrofit,但是,retrofit内部其实也是用了okhttp;
1.使用方法:
(1)创建一个请求客户端okhttpClient对象
(2)创建一个请求Request对象,通过Build模式创建
(3)创建一个实际的http请求call对象,它可以调用execute(同步获取数据),也可以调用enqueue(异步获取数据);
public class OkhttpFace extends Activity { //第一步:创建一个okHttpClient private final OkHttpClient client=new OkHttpClient(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } private void okhttpAsyRequest() throws Exception{ //第二步:创建一个Request,通过Builder模式生成 Request request=new Request.Builder().url("http://www.baidu.com").build(); //第三步,通过client的call方法创建一个call对象,代表实际的http请求 Call call = client.newCall(request); //同步获取数据 Response response = call.execute(); //获取请求头 Headers headers=response.headers(); Log.e("请求数据为",response.body().toString()); } private void okhttpSynRequest(){ //第二步:创建一个Request,通过Builder模式生成 Request request=new Request.Builder().url("http://www.baidu.com").build(); //第三步,通过client的call方法创建一个call对象,代表实际的http请求 Call call = client.newCall(request); //异步获取数据 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { //获取请求头 Headers headers=response.headers(); Log.e("请求数据为",response.body().toString()); } }); } }
这里的同步获取数据指的是,当调用execute方法是,它会阻塞线程去获取数据,异步获取数据指的是,它会开启一个新的线程,在新的线程中去获取数据,其中的callback回调都是工作在新线程中的;
2.源码分析:
我们先来看同步获取数据:
首先第一步没啥好说的,使用okhttp,每一个请求都需要一个client客户端,所以,你可以把它搞成全局的final对象;
第二步是采用Build模式新建一个request请求,这里提一下,build模式其实在开源框架中使用的很频繁,它的主要作用就是讲一个复杂的对象的创建和显示分离开来;
第三步call.execute方法,我们点进去看看:(其实真正调用execute的不是call,而是Realcall)
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } }
在execute方法中,首先它会做一个同步的检查,判断这个请求是否已经被执行了,因为每一个请求只能被执行一次,接下来,它会调用client的dispatcher()方法去执行网络请求,这里的dispatcher其实在它的官方文档上写的是执行异步操作的策略,但是,现在在同步里出现了,我们先不去关注他,因为一会的异步会出现很多这个dispatcher;继续往下看,getResponseWithInterceptorChian()这个方法可以说把okhttp最精髓的地方体现出来了,也就是----拦截器,最后,它会返回请求到的数据,然后把这次的请求关闭;
其实,真正做网络请求的,就是getResponseWithInterceptorChian()这个方法,我们需要看看:
private Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. Listinterceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (!retryAndFollowUpInterceptor.isForWebSocket()) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor( retryAndFollowUpInterceptor.isForWebSocket())); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); }
这么多的interceptor,没错,这就是拦截器,okhttp最大的亮点就是这个interceptor,网络请求要很多步骤,比如需要去请求网络,缓存,透明压缩,等等一系列的操作,okhttp这个网络框架就把这些操作分割成一个个的interceptor,然后把这些interceptor连成一个链也就是Interceptor.Chain,然后去完成一次完整的网络请求,这是典型的分层思想的提现,把一次复杂的工作分步骤去做,更加合理,也更加高效安全;
就比如上面的代码中,一次请求就添加了失败重定向拦截器,桥接拦截器,缓存拦截器,连接网络的拦截器,连接服务器的拦截器,这些拦截器组成一条完整的链,这里,我们选一个拦截器看看内部是什么,我们就选这个CallServerInterceptor
@Override public Response intercept(Chain chain) throws IOException { HttpStream httpStream = ((RealInterceptorChain) chain).httpStream(); StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation(); Request request = chain.request(); long sentRequestMillis = System.currentTimeMillis(); httpStream.writeRequestHeaders(request); if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength()); BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } httpStream.finishRequest(); Response response = httpStream.readResponseHeaders() .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); if (!forWebSocket || response.code() != 101) { response = response.newBuilder() .body(httpStream.openResponseBody(response)) .build(); } if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) { streamAllocation.noNewStreams(); } int code = response.code(); if ((code == 204 || code == 205) && response.body().contentLength() > 0) { throw new ProtocolException( "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength()); } return response; }这个方法内部其实就是取得请求request的头部和body,然后取得response的头部和body,大致就是这么一个流程,但是在这里,我们注意一个类,
HttpStream httpStream = ((RealInterceptorChain) chain).httpStream();
HttpStream类,这个类点进去看啊,它其实是集成了okio,我们又知道okio内部其实就是socket,所以我们就可以断定okhttp底层其实就是socket;
接下来,我们再来看看okhttp的异步请求的源码:
其实异步与同步最大的不同就是call调用的方法不一样,我们进enqueue方法看看
@Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
这里我们发现,有事dispatcher,好吧,终究还是跑不掉,进去看看吧:
在dispatcher里,其实我们能看见三个非常重要的集合类:
/** Ready async calls in the order they'll be run. */ private final DequereadyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningSyncCalls = new ArrayDeque<>();
第一个是正在准备的异步请求
第二个是正在运行的异步请求,
第三个是正在运行的同步请求;
了解了这些之后,我们再看看dispatch的enqueue方法
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
从这里我们就可以看出他是怎么实现异步的了,首先它会判断当前的请求是否符合某些特定的要求,如果满足,那么就直接把这个请求添加到正在运行的异步请求集合里,并直接执行网络请求,如果不满足,那么就会把它加入到正在准备的异步请求集合中;其实在这里的executorService()内部就是一个线程池
public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue说到底,异步和同步其实就是在处理多个请求时,同步是将其阻塞,异步是封装一个线程池,最后真正去执行网络请求的其实还是interceptor。(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }