Android Volley框架使用源码分享

时间:2021-05-21

过去在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队列中处理,如果有缓存那么就直接获取缓存了,之后判断当前的请求中是否有相同的请求,如果有的话那么就把这个请求加入到暂存集合中,如果没有那么就加入一个空的到请求到暂存队列中,用来以后判断是否有和这个请求相同的请求,然后加入缓存队列中即可。

然后我们来看RequstQueue的创建过程

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://WorkQueue 等待调用

以上就是本文的全部内容,希望对大家学习Android Volley框架有所帮助。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章