OKHttp原码分析(八)之必须明白的几个问题
2017-05-09 20:44
330 查看
一,概述
截止到上一篇blog,OKHttp的源码分析已结束,由于篇幅有限,个人能力不足,分析不够细致,也不够完美,希望小伙们多多指正。这篇blog以带着问题找答案的方式再次回顾下OKHttp的源码。
首先提出以下几个问题:
Okhttp的异步请求是怎么开启子线程的?
异步请求时回调方法在哪被调用的?回调方法执行在哪个线程?
Okhttp上传文件的原理。
OKHttp下载文件的原理,是否支持断点续传?
Okhttp的网络请求的本质。
socket中不区分响应头和响应体,为什么底层封装socket的OKHttp就区分了响应头和响应体?
二,Okhttp的异步请求是怎么开启子线程的?
这个问题 答案主要看OKHttp原码分析(一)中的第六点:RealCall的enqueue方法实现异步请求。在RealCall的enqueue方法中调用了Dispatcher的enqueue方法,在Dispatcher的enqueue方法中有下面这行代码:
executorService().execute(call);
这行代码中的executorService()方法得到一个线程池对象,然后调用线程池对象的execute方法,此时call对象的run方法就行在了子线程中。
executorService()方法的源码是:
public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60,TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; }
由源码可知此时创建了一个ThreadPoolExecutor对象。这个线程池的特点是:
1,核心线程数为0。
2,最大线程数是Integer的最大值,这里可以理解为无限多。
3,非核心线程存活时间为60秒。
总结:okhttp的异步请求是在Dispatcher的enqueue方法中使用线程池开启的子线程。
三,异步请求时回调方法在哪被调用的?回调方法执行在哪个线程?
这个问题 还需要看OKHttp原码分析(一)中的第六点:RealCall的enqueue方法实现异步请求。RealCall的enqueue方法的源码如下:
@Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
我们看下AsyncCall类。
AsyncCall类是RealCall的内部类,AsyncCall继承NamedRunnable类,NamedRunnable又继承Runnable类。在NamedRunnable类中声明了一个抽象方法execute(),且在run方法中调用了。我们由代码executorService().execute(call)知道此时会调用run方法,从而会调用execute方法。
下面看AsyncCall的execute方法的源码(注意:这个方法执行在子线程):
@Override protected void execute() { boolean signalledCallback = false; try { Response response = getResponseWithInterceptorChain(); if (retryAndFollowUpInterceptor.isCanceled()) { responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { responseCallback.onResponse(RealCall.this, response); } } }
此时发现,原来回调方法在这被调用的啊。
总结:回调方法在AsyncCall的execute方法中被调用,即是在AsyncCall的run方法中被调用。而AsyncCall是Runnable的子类,所以run方法会执行在子线程,所以回调方法也执行在子线程中。
四, Okhttp上传文件的原理。
使用OKHttp上传文件首先将文件封装到RequestBody中,创建带有文件的RequestBody使用的代码是:MediaType fileType = MediaType.parse("File/*");//数据类型为json格式, File file = new File("path");//file对象. RequestBody body = RequestBody.create(fileType , file );
下面查看RequestBody类create方法的源码:
public static RequestBody create(final MediaType contentType, final File file) { if (file == null) throw new NullPointerException("content == null"); return new RequestBody() { @Override public MediaType contentType() { return contentType; } @Override public long contentLength() { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { Source source = null; try { source = Okio.source(file); sink.writeAll(source); } finally { Util.closeQuietly(source); } } }; }
写文件的核心代码是:
source = Okio.source(file);//根据文件得到输入流对象。 sink.writeAll(source);//将输入流对象写出去。
writeAll是RealBufferSink类的方法,这个也是属于Okio框架中的。方法的原码是:
@Override public long writeAll(Source source) throws IOException { long totalBytesRead = 0; for (long readCount; (readCount = source.read(buffer, Segment.SIZE)) != -1; ) { totalBytesRead += readCount; emitCompleteSegments(); } return totalBytesRead; }
emitCompleteSegments方法的源码是:
@Override public BufferedSink emitCompleteSegments() throws IOException { if (closed) throw new IllegalStateException("closed"); long byteCount = buffer.completeSegmentByteCount(); if (byteCount > 0) sink.write(buffer, byteCount); return this; }
此时发现:上传文件就是使用的输出流的write方法,且使用字节数组的方式写出,字节数组的长度是:Segment.SIZE = 8192。
注意:使用OKHttp上传文件是直接写入,并不支持断点续传。
五,OKHttp下载文件的原理,是否支持断点续传?
在OKHttp使用详解中讲到OKHttp中并没有提供下载文件的功能,但是在Response中可以获取流对象,有了流对象之后我们自己实现文件的下载。代码如下:try{ InputStream is = response.body().byteStream();//从服务器得到输入流对象 long sum = 0; File dir = new File(mDestFileDir); if (!dir.exists()){ dir.mkdirs(); } File file = new File(dir, mdestFileName);//根据目录和文件名得到file对象 FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[1024*8]; int len = 0; while ((len = is.read(buf)) != -1){ fos.write(buf, 0, len); } fos.flush(); return file; }
总结:OKHttp并没有封装文件的下载功能,需要自己去实现。所以要想支持断点续传功能也需要自己去实现。
六,Okhttp的网络请求的本质
在OKHttp原码分析(五)之Interceptor中的第三条,CallServerInterceptor类分析中讲到:网络请求的本质是CallServerInterceptor类中的intercept方法。这个方法写入了请求头,写入了请求体,得到了响应头,得到了response对象。但是这些操作都是调用的httpStream类中的方法,在httpStream类的方法中先通过RealConnection对象得到流对象,再调用RequestBody类的方法进行写操作。所以网络请求的本质是在RealConnection类中。所以问题的答案在OKHttp原码分析(六)之RealConnection中。我们看RealConnection类的connectSocket方法的源码:
private void connectSocket(int connectTimeout, int readTimeout) throws IOException { Proxy proxy = route.proxy(); Address address = route.address(); ;//得到socket对象 rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP ? address.socketFactory().createSocket() : new Socket(proxy); rawSocket.setSoTimeout(readTimeout); try { ;//连接socket Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); } catch (ConnectException e) { throw new ConnectException("Failed to connect to " + route.socketAddress()); } source = Okio.buffer(Okio.source(rawSocket));//从socket中获取source 对象。 sink = Okio.buffer(Okio.sink(rawSocket));//从socket中获取sink 对象。 }
这个方法里做了三件事情:
1,创建socket对象。
2,连接socket。
3,使用okio包从socket中得到source 对象和sink 对象。
总结:okhttp的网络请求的本质在RealConnection类的connectSocket方法中。这里创建了socket对象,使用socket连接服务器,所以okhttp的网络请求的本质是封装的socket。与httpURLconnection没有任何关系。
七, socket中不区分响应头和响应体,为什么底层封装socket的OKHttp就区分了响应头和响应体?
这个问题的答案在OKHttp原码分析(七)之HttpStream中的第八条,OKHttp是怎么区分的响应头与响应体。总结来说是:OKHttp在封装socket时遵从的是http协议,在http协议中规定,将数据分成两份,且以\n为分界点,\n之前的数据属于响应头。\n之后的数据属于响应体。服务器和客户端都遵从这个协议,就能区分出响应头和响应体了。
相关文章推荐
- 聊聊软件与吃饭(二)- 谈谈甲方选择软件供应商必须要分析的几个问题
- Handler运行机制中必须明白的几个问题
- 绝对能够测试你的C语言功力的几个问题及其讲解分析
- cookie与session性能分析与安全性分析及几个小问题
- .NET 多线程必须搞清楚的几个问题
- 故障处理:这几天发现的几个WAYOS故障问题的现象及分析,解决方案
- 分析几个我在加载动态字符驱动模块时候遇到的问题
- DataGrid 分页必须考虑的几个问题
- 分析HM代码,首先必须把class TComDataCU完全搞明白!
- 高速信号分析的几个基本问题
- JAVA初学者必须搞懂的几个问题!
- 几个ajax js性能优化和内存泄露问题及检测分析工具
- 创业前必须思考的几个问题
- Hibernate必须知道的几个问题
- 网页设计-[常用CSS缩写语法总结、标准的N种死法及网页设计必须注意的几个问题 ]
- 数据分析应注意的几个问题
- 关于考试类型和成绩分析的几个问题
- java入门必须懂的几个问题
- 网页设计必须注意和掌握的几个问题
- PHP+新浪微博开放平台+新浪云平台(SAE)开发微博应用——必须交待的几个问题