以前在android上网络通信都是使用的Xutils 因为用它可以顺道处理了图片和网络这两个方面,后来发觉Xutils里面使用的是HttpClient 而Google在6.0的版本上已经把HttpClient废除了,所以开始寻找新的网络框架,okhttp也用过,但是它是在作用在UI线程,使用起来还需要用handler 所以就先用着Volley框架了。 这里我先分析下Volley框架的简单网络请求的源码。
使用Volley请求网络数据的简单过程:
RequestQueue queue = Volley.newRequestQueue(this); //实例化一个请求队列 Google推荐写一个单例类 获取唯一一个队列 StringRequest request = new StringRequest(Request.Method.POST, url1, new Response.Listener<String>() { @Override public void onResponse(String response) { Toast.makeText(MainActivity.this, "success"+response, Toast.LENGTH_SHORT).show(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Toast.makeText(MainActivity.this, "失败了"+error.getMessage(), Toast.LENGTH_SHORT).show(); } }){ @Override protected Map<String, String> getParams() throws AuthFailureError { //重写这个函数提交参数 也可以重写一个Request实现这个方法 Map<String,String> params = new HashMap<>(); params.put(aaa+"name","1233555"); //参数 return params; } }; queue.add(request);请求的处理在newRequestQueue的时候就开始执行了 只不过那时候请求队列中还没有请求 所以阻塞了 当 add的方法执行时 才开始真正请求网络
所以我们先来看 queue.add(request) 方法
public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { mCurrentRequests.add(request); //在当前队列中加入 } // Process requests in the order they are added. request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue"); //设置标志 // If the request is uncacheable, skip the cache queue and go straight to the network. if (!request.shouldCache()) { //根据是否需要缓存 如果不需要缓存 就直接加入网络任务队列中 然后返回 如果需要缓存 那么在下面代码中加入缓存队列 默认是需要缓存的 mNetworkQueue.add(request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { //判断当前正在被处理并可以缓存的请求中是否包含该请求的key 如果包含说明已经有一个相同的请求 那么就加入到其中 // There is already a request in flight. Queue up. Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { //如果不包含 加入一个空的请求到 暂存队列中 然后加入到缓存队列中 // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } }分析add方法 首先加入到mCurrentRequests集合中 这个集合存放所有这个队列所处理的请求 然后判断这个请求是否需要缓存,如果不需要缓存,那么直接加入mNetworkQueue队列中等待处理即可,如果需要那么最终加入到mCacheQueue队列中,因为RequestQueue在处理请求时总会先处理缓存的任务,在处理缓存时如果第一次处理没有缓存还是会加入mNetworkQueue队列中处理,如果有缓存那么就直接获取缓存了,之后判断当前的请求中是否有相同的请求,如果有的话那么就把这个请求加入到暂存集合中,如果没有那么就加入一个空的到请求到暂存队列中,用来以后判断是否有和这个请求相同的请求,然后加入缓存队列中即可。
public static RequestQueue newRequestQueue(Context context, HttpStack stack) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); //创建一个文件用于缓存 String userAgent = "volley/0"; //用户代理初始化 try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; //用户代理为app包名+版本号 } catch (NameNotFoundException e) { } if (stack == null) { //如果没传入HttpStack 那么采用下述默认的 这里可以自行重写扩展HttpStack 体现了该框架的高扩展性 if (Build.VERSION.SDK_INT >= 9) { //如果sdk版本高于2.3 采用HurlStack 内部是httpUrlConnection实现 stack = new HurlStack(); } else { //如果版本低于2.3 采用httpClientStack // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); //创建一个网络工作 仅仅作用于请求网络 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); //实例化一个请求队列 传入参数 queue.start(); return queue; }
</pre><pre code_snippet_id="1680121" snippet_file_name="blog_20160512_5_2241745" name="code" class="java">public RequestQueue(Cache cache, Network network, int threadPoolSize) { //构造函数 会创建默认的ExecutorDelivery 用于回调 this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); }RequestQueue的创建过程也比较简单 根据sdk版本号判断使用HttpURLConnection还是HttpClient 因为在2.3之前 httpUrlConnection有一个重大的bug 所以使用HttpClient代替,而httpUrlConnection体积小 支持gzip压缩和缓存,并且速度相对httpClient快 并逐渐优化 所以选择httpUrlConnection 之后根据创建的NetWork 创建RequestQueue队列 然后开启即可
之后我们查看 queue的start方法
public void start() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); //创建一个缓存调度器 是一个线程 start后执行run方法 mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < mDispatchers.length; i++) { //默认会有4个NetworkDispatcher 为了提高效率 执行netWorkQueue里的request NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }这个方法 先执行缓存调度器线程然后执行4个网络工作调度器线程,因为在缓存调度器中 会判断是否缓存过,如果缓存过并且没过期,就直接复用缓存的,不把任务加入netWordQueue中 所以下面的NetWork调度器线程就会取不到请求而阻塞,不会执行,而如果没有缓存,缓存调度器线程中就会把请求加入NetWork队列中,下面的netWork调度器就会取到该请求并执行了
我们仔细看一下CacheDispatcher线程的源码:
run方法的代码比较长 我们分开来看 先看第一部分:
@Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //设置线程的优先级 值为10 // Make a blocking call to initialize the cache. mCache.initialize(); //初始化一下缓存 while (true) { try { // Get a request from the cache triage queue, blocking until // at least one is available. final Request<?> request = mCacheQueue.take(); //从缓存队列取出一个请求 如果没有则会阻塞 request.addMarker("cache-queue-take"); //添加一个标记 // If the request has been canceled, don't bother dispatching it. if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } // Attempt to retrieve this item from cache. Cache.Entry entry = mCache.get(request.getCacheKey()); //从缓存中读取缓存 if (entry == null) { //如果没读取到缓存 request.addMarker("cache-miss"); //添加缓存miss标记 // Cache miss; send off to the network dispatcher. mNetworkQueue.put(request); //换区缓存失败 添加到netWork中等待请求 continue; } // If it is completely expired, just send it to the network. if (entry.isExpired()) { //判断缓存是否过期了 如果过期了 那么就添加到netWork中等待请求 request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; }第二部分 :
// We have a cache hit; parse its data for delivery back to the request. request.addMarker("cache-hit"); //执行到了这里说明缓存没有过期 并且可以使用 Response<?> response = request.parseNetworkResponse( //把读取到的缓存内容解析成Response对象 new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); //添加标记 if (!entry.refreshNeeded()) { //如果缓存不需要刷新 直接调用 mDelivery.postResponse方法 在其中会回调request的listener接口 // Completely unexpired cache hit. Just deliver the response. mDelivery.postResponse(request, response); } else { //如果需要刷新 把请求加入mNetworkQueue中 等待请求 // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // Post the intermediate response back to the user and have // the delivery then forward the request along to the network. mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }
上面代码的具体过程也很简单 首先从缓存请求队列取出一个请求,在缓存中看看有没有该请求的缓存,如果没有 那么 请求放入NetWork调度器中 等待调用 如果有 也分几种情况 如果获取到的是空,放入NetWOrk 如果过期 放入 NetWork 如果不需要刷新 就直接从缓存获取响应信息并解析 然后用mDelivery回调接口即可 如果需要刷新 放入NetWOrd队列等待调用。。。
我们再来看看NetworkDispatcher 线程的代码就可以了 类似于CacheDispatcher的代码:
@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //设置优先级 10 while (true) { long startTimeMs = SystemClock.elapsedRealtime(); //获取请求执行开始时间 Request<?> request; try { // Take a request from the queue. request = mQueue.take(); //从队列获取一个请求 没有则阻塞 } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); //真正执行请求的函数 并返回响应 request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, // we're done -- don't deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse); //解析响应 request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { //如果需要缓存 那么把响应的信息存入缓存中 mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered(); mDelivery.postResponse(request, response); //之后回调一些方法 } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); //回调错误接口 } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); //回调错误接口 } } }
NetworkDispatcher 线程的执行过程 先从 networkDispatch中获取一个请求 然后判断 是否取消了 如果没有 那么就执行NetWOrk的performRequest方法 执行http请求,这个函数内部才是真正的请求数据 ,请求后 根据设置的shouldCache标志 判断是否放入缓存中 之后回调一些接口方法 即可 这样就完成了一个请求
最后我们看一看NetWork类mNetwork.performRequest(request)方法是如何提交请求的吧 代码比较长 但是不难:
@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); //记录开始时间 while (true) { HttpResponse httpResponse = null; byte[] responseContents = null; Map<String, String> responseHeaders = Collections.emptyMap(); //初始化响应头为空 try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); //请求头 addCacheHeaders(headers, request.getCacheEntry()); //根据缓存添加请求头 httpResponse = mHttpStack.performRequest(request, headers); //调用HttpStack的方法请求网络 StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); //获取响应头 // Handle cache validation. if (statusCode == HttpStatus.SC_NOT_MODIFIED) { //如果为304 读取的缓存 Entry entry = request.getCacheEntry(); //查看以前是否缓存过 if (entry == null) { //如果以前缓存的为空 那么 说明上次缓存的请求也为空 直接返回response return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // A HTTP 304 response does not have all header fields. We // have to use the header fields from the cache entry plus // the new ones from the response. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 entry.responseHeaders.putAll(responseHeaders); //如果不空 那么就添加头 然后返回 数据了 return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) { //不是304的情况
responseContents = entityToBytes(httpResponse.getEntity()); //获取响应的内容 下面返回响应即可 } else { // Add 0 byte response as a way of honestly representing a // no-content request. responseContents = new byte[0]; } // if the request is slow, log it. long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); } catch (SocketTimeoutException e) { attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException e) { attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } }然后看 HttpStack的 请求代码:
@Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); //添加请求头 map.putAll(additionalHeaders); 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 connection = openConnection(parsedUrl, request); //打开连接 for (String headerName : map.keySet()) { //设置头 connection.addRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); //在这个函数里添加请求的参数 和一些基本的信息配置 // 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); 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; }
case Method.POST: connection.setRequestMethod("POST"); addBodyIfExists(connection, request); break;
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError { byte[] body = request.getBody(); if (body != null) { connection.setDoOutput(true); connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(body); out.close(); } }
public byte[] getBody() throws AuthFailureError { Map<String, String> params = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; }getParams方法 是Request需要重写的一个方法 返回值就是参数的Map集合
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) { StringBuilder encodedParams = new StringBuilder(); try { for (Map.Entry<String, String> entry : params.entrySet()) { encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding)); encodedParams.append('='); encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding)); encodedParams.append('&'); } return encodedParams.toString().getBytes(paramsEncoding); } catch (UnsupportedEncodingException uee) { throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee); } }这个函数就是按照一定规则拼接字符串参数即可 然后 就可以提交参数了
最后介绍下这个框架主要的几个类、成员及他们作用:
RequestQueue 用来处理请求的队列,请求都放在这个类中 调用start方法 开始处理请求
mCache 请求的缓存,当提交了一个请求 并且此请求需要缓存时,会放入这个缓存中
mNetwork 单纯用于提交网络请求的接口 只有一个提交请求的方法 需要传入一个HttpStack来完成请求的提交
mDelivery 用于请求响应后的 接口回调等功能
mDispatchers NetWork调度器线程数组 包含4个对象处理请求 目的是为了提高效率 当没有缓存可以获取或者已经过期 需要刷新时 会调用这个线程的run方法 如果没有 则阻塞
mCacheDispatcher 缓存调度器线程 处理已经缓存了的请求 如果没有缓存 则将请求放入 NetWorkQueue 等待调用