Volley尝试源码解析1
来源:互联网 发布:网络划分的策略 编辑:程序博客网 时间:2024/06/09 21:06
Volley源码分析一
这是做Android开发以来第一次分析网络框架,虽然项目做了不少,但是之前都是在公司项目封装好的基础框架上做开发,网络架构也早已经被公司大牛封装好了,所以请求网络对于我们这种拿来主义的人来说,就是简单的调用方法,穿几个参数,写几个bean,但是but这样时间长了真的好吗?本人得出的答案是不好,所以打算写一个Volley框架源码解析的系列。或许就有人说了,网上Volley源码解析一搜一大堆,还有必要再造一次轮子吗,这不是吃力不讨好,但是本人的目的不在于有没有人看,而是对自己的一个要求。本系列就是拿出其中一些类一个一个的分析,期待最终会有多大的收获。
- RequestQueue
这个用过Volley框架的人都知道,这里第一个分析它,应该不用过多解释吧,好了,Volley三部曲:第一曲,Volley.newRequestQueue(Context context)得到RequestQueue(我把这个类比作是一个黑盒子,有request请求就把它丢到这个黑盒子里,但是这个黑盒子很神奇(由于水平有限,很多东西不能分析的非常到位,但是作为初级水平来说,如果看源码或者框架如果一下子看的非常细,往往是有难度的,所以这个时候如果故意的模糊一些东西,比如这里被称为黑盒子的东西,把一个大的东西的流程或者主线看懂,再慢慢的攻克这些黑盒子,最终一定会有惊喜),经过它之后,我们就能得到想要的数据。先贴出源码
public class Volley { /** Default on-disk cache directory. */ //默认的缓存路径 private static final String DEFAULT_CACHE_DIR = "volley"; /** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * 创建一个默认的线程池实力并且调用start()方法 * You may set a maximum size of the disk cache in bytes. *或许你应该设置一个最大的缓存区大小 * @param context A {@link Context} to use for creating the cache dir. * 上下文环境,用于创建缓存目录 * @param stack An {@link HttpStack} to use for the network, or null for default. * HttpStack对象用来执行网络请求操作,默认可以为空 * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size. * 缓存区大小,传入-1代表使用默认大小 * @return A started {@link RequestQueue} instance. * 返回RequestQueue对象 */ public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);//创建缓存区目录 String userAgent = "volley/0"; //网络的一个请求头(就知道这么多),一般设置为应用的packageName + "/"+versionCode try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null) { //如果传入的HttpStack为空,执行下面的操作,生成一个HttpStack对象 if (Build.VERSION.SDK_INT >= 9) { //只分析这种情况,HttpClient在高版本中被废弃了,现在都是用httpurlconnection stack = new HurlStack(); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } }**第一篇准备就到这里打住了,接下来看看HurlStack是何方圣神** //Network network = new BasicNetwork(stack); // RequestQueue queue; //if (maxDiskCacheBytes <= -1) // { // No maximum size specified // queue = new RequestQueue(new //DiskBasedCache(cacheDir), network); // } // else // { // Disk cache size specified // queue = new RequestQueue(new //DiskBasedCache(cacheDir, maxDiskCacheBytes), network); // } // queue.start(); // return queue; } /** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * You may set a maximum size of the disk cache in bytes. * * @param context A {@link Context} to use for creating the cache dir. * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size. * @return A started {@link RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) { return newRequestQueue(context, null, maxDiskCacheBytes); } /** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * * @param context A {@link Context} to use for creating the cache dir. * @param stack An {@link HttpStack} to use for the network, or null for default. * @return A started {@link RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context, HttpStack stack) { return newRequestQueue(context, stack, -1); } /** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * * @param context A {@link Context} to use for creating the cache dir. * @return A started {@link RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); }
2.HurlStack
还是老套路,先上代码
/** * An {@link HttpStack} based on {@link HttpURLConnection}.类的注释写的真好,看到了熟悉的东西了HttpURLConnection,猜测发送网络请求的任务应该都落在了HttpStack的身上,到底猜测的对不对,我们接下来分析得出结论 */public class HurlStack implements HttpStack { *HttpStack 怎么出来就是实现了一个接口啊,有点慌,这个类不会很复杂吧,赶紧cmd进入HttpStack,看看这个接口到底是个什么鬼,进去一看,终于缓了一口气,接口太简单,就不贴出来了* private static final String HEADER_CONTENT_TYPE = "Content-Type"; //又是网络请求的一个头部参数,我就是不懂,咋的 /** * An interface for transforming URLs before use. * 在使用url之前对url做一次转换(什么转换,天知道我不知道,不过看接口我发现我想怎么转换就怎么转换,无非就是string进去string出来。嘎嘎嘎) */ public interface UrlRewriter { /** * Returns a URL to use instead of the provided one, or null to indicate * this URL should not be used at all. */ public String rewriteUrl(String originalUrl); } private final UrlRewriter mUrlRewriter; private final SSLSocketFactory mSslSocketFactory; //这个又是什么鬼,我又不知道怎么办,管它呢,暂时就把它当个鬼 public HurlStack() { //管用方式,无参数的方法调用有参数的方法 this(null); } /** * @param urlRewriter Rewriter to use for request URLs */ public HurlStack(UrlRewriter urlRewriter) { this(urlRewriter, null); } /** * @param urlRewriter Rewriter to use for request URLs * @param sslSocketFactory SSL factory to use for HTTPS connections */ public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) { mUrlRewriter = urlRewriter; mSslSocketFactory = sslSocketFactory; }**碰到了一堆鬼,鬼太厉害了,还好不惹他,他也不会影响你,保持好的心情进入真正的主角** //实现了接口方法,上面实现的那个接口,唯一的作用就是提供了这么一个方法,接收两个参数,1 Request(这不就是Volley二部曲的Request么,恩答案是对的) 2 Map<String, String> additionalHeaders 看参数名称就知道是网络请求的请求头 @Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { //每个request都有一个url吧,毫无疑问 String url = request.getUrl(); //实例化一个map集合,作用是存放请求头信息 HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); //一个鬼来了,发现它的作用真的就是对url做一些处理,大部分的时候我们给的url是不需要做什么特殊处理的,想象一个场景,如果一个项目中突然所有请求的url要统一做一种处理,这个时候这个鬼的作用是不是就体现出来了 if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL(url); //终于看到老朋友了HttpURLConnection,这里通过调用openConnection()方法得到HttpURLConnection对象,迫不及待的想去看看这个方法里到底做了哪些操作,嗖的一下就跳到了该方法 HttpURLConnection connection = openConnection(parsedUrl, request); //这不又在这里设置请求头了 for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } //上面还在想,请求头设置完成了,是不是该轮到请求正文了,果不其然,来了,手一抖不小心一下子就到方法里了 setConnectionParametersForRequest(connection, request); //接下来就是对返回报文的处理了,返回HttpResponse对象,到这里这个类基本就算分析完了 // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = connection.getResponseCode(); if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) { response.setEntity(entityFromConnection(connection)); } for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); response.addHeader(h); } } return response; } /** * Checks if a response message contains a body. * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a> * @param requestMethod request method * @param responseCode response status code * @return whether the response has a body */ private static boolean hasResponseBody(int requestMethod, int responseCode) { return requestMethod != Request.Method.HEAD && !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK) && responseCode != HttpStatus.SC_NO_CONTENT && responseCode != HttpStatus.SC_NOT_MODIFIED; } /** * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. * @param connection * @return an HttpEntity populated with data from <code>connection</code>. */ private static HttpEntity entityFromConnection(HttpURLConnection connection) { BasicHttpEntity entity = new BasicHttpEntity(); InputStream inputStream; try { inputStream = connection.getInputStream(); } catch (IOException ioe) { inputStream = connection.getErrorStream(); } entity.setContent(inputStream); entity.setContentLength(connection.getContentLength()); entity.setContentEncoding(connection.getContentEncoding()); entity.setContentType(connection.getContentType()); return entity; } /** * Create an {@link HttpURLConnection} for the specified {@code url}. */ protected HttpURLConnection createConnection(URL url) throws IOException { return (HttpURLConnection) url.openConnection(); } /** * Opens an {@link HttpURLConnection} with parameters. * @param url * @return an open connection * @throws IOException */ private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException { //调用createConnection()方法得到connection对象 HttpURLConnection connection = createConnection(url); //对connection对象做一一系列的属性设置,还是那句话,不懂我就是不懂这些属性干嘛的,咋的 int timeoutMs = request.getTimeoutMs(); connection.setConnectTimeout(timeoutMs); connection.setReadTimeout(timeoutMs); connection.setUseCaches(false); connection.setDoInput(true); // use caller-provided custom SslSocketFactory, if any, for HTTPS if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) { ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory); }//到这里终于看到了我想要返回的对象,真是开心啊,回到前面继续往下走 return connection; } @SuppressWarnings("deprecation") /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { switch (request.getMethod()) { //首先判断请求类型,get 、post、啥的一大堆,不好意思,到目前为止也就只接触了这两个东西,屌丝就屌丝吧,接下来这么多分枝里我要找到一个我熟悉的它 case Method.DEPRECATED_GET_OR_POST: // This is the deprecated way that needs to be handled for backwards compatibility. // If the request's post body is null, then the assumption is that the request is // GET. Otherwise, it is assumed that the request is a POST. byte[] postBody = request.getPostBody(); if (postBody != null) { // Prepare output. There is no need to set Content-Length explicitly, // since this is handled by HttpURLConnection using the size of the prepared // output stream. connection.setDoOutput(true); connection.setRequestMethod("POST"); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(postBody); out.close(); } break; case Method.GET: // Not necessary to set the request method because connection defaults to GET but // being explicit here. connection.setRequestMethod("GET"); break; case Method.DELETE: connection.setRequestMethod("DELETE"); break; case Method.POST: //是它是它就是它 connection.setRequestMethod("POST"); //设置请求类型 addBodyIfExists(connection, request); //为嘛不把逻辑直接在这里写了,没事让我又要收哆嗦一下 break; case Method.PUT: connection.setRequestMethod("PUT"); addBodyIfExists(connection, request); break; case Method.HEAD: connection.setRequestMethod("HEAD"); break; case Method.OPTIONS: connection.setRequestMethod("OPTIONS"); break; case Method.TRACE: connection.setRequestMethod("TRACE"); break; case Method.PATCH: connection.setRequestMethod("PATCH"); addBodyIfExists(connection, request); break; default: throw new IllegalStateException("Unknown method type."); } } private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { byte[] body = request.getBody(); //这个在之后才会分析,这里直接给出结论,通过request.getBody()得到a="a"&b="b"这种形式的字符串的字节数组 if (body != null) { //请求正文不为null,将请求正文发送给服务器 connection.setDoOutput(true); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(body); out.close(); } }}
总结:到目前为止,看了两个类RequestQueue(分析了部分) ;HurlStack类,知道了这个类的核心作用主要就是实例化HttpURLConnection对象对请求报文和返回报文的处理,其中有许多请求报文头的属性真的也不是很清楚,这也是接下来需要学习的东西之一,但是这并不影响代码的阅读,有些东西不能太较真,只要把重要的整明白就是好的,只有在不断的学习中才能知道自己不懂得东西,才能有方向的学习。
好了,不啰嗦了,洗洗睡觉了。最后声明一句:本人能力有限,写的东西不一定正确,希望如果不小心被你看到了,发现了问题,请赐教,因为本人现在写博客的目的是做笔记,方便自己的学习,能帮助到别人自然更好了。
- Volley尝试源码解析1
- Volley 尝试源码解析2
- volley尝试源码解析3
- TODO 阅读volley源码 尝试进行volley源码解析
- Volley源码解析 --- Volley组成(1)
- 【Volley】Volley源码解析
- volley源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- 马云---下一个三十年企业家最重要的素质是什么?
- 【PAT甲级】1049. Counting Ones (30)
- django 1.8 官方文档翻译:logging
- Android快速自定义倒计时按钮
- EasyDarwin+ffmpeg进行PC(摄像头+麦克风)流媒体直播服务
- Volley尝试源码解析1
- 关于adb端口被占用
- (2016弱小联盟十一专场10.3) Help the Princess! BFS
- 学习:实现任意角度的sin、cos求值-No.1
- 正则表达式判断手机号
- 如何控制秒杀商品页面购买按钮的定时点亮
- Go语言编程入门
- ffmpeg推送,EasyDarwin转发,vlc播放 实现整个RTSP直播
- SWT常用组件列表及使用