• Android网络通信Volley框架源代码浅析(一)


    尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897


           从今天開始,我打算为大家呈现关于Volley框架的源代码分析的文章,Volley框架是Google在2013年公布的。主要用于实现频繁并且粒度比較细小的Http请求。在此之前Android中进行Http请求一般是使用HttpUrlConnection和HttpClient进行,可是使用起来非常麻烦,并且效率比較地下,我想谷歌正式基于此种原因公布了Volley框架。事实上出了Volley框架意外,也有一些http请求开源项目。比方使用比較广泛的有async-http,UniversImageLoader等等,当中async-http主要用来实现异步http请求,而后者主要用来请求图片。Volley具有以上两种框架的功能,并且是Google公司公布。我想作为Android开发人员,非常有必要研究一下该框架。


    1、下载Volley框架
    git clone https://android.googlesource.com/platform/frameworks/volley

    2、引用Volley框架
    引用该框架的方式主要有两种:
    (1):直接导入Volley框架,作为lib使用
    (2):编译Volley成jar包

    3、Volley的功能:
    前面已经提及了Volley是一个用于http请求的框架,其主要功能例如以下:
    json,xml,String,Image等资源的请求,当然我们还能够依据自己的须要来改写Volley框架源代码。从而实现自己的功能


    4、Volley源代码分析

    温馨提醒:假设是第一次看Volley源代码,第一遍没看懂没关系,将源代码copy下来,跟着我的思路慢慢分析,将文章从头到后多看几遍就ok了。由于Volley的一些关键类都互对应用。我仅仅能一个一个的分析了,等你看完我全部的文章,然后再从头看一变。相信你定有所收获


    当然阅读次文章之前。最好学会知道Volley框架的基本使用,由于网络上非常多相似的教程。我在此处就不再描写叙述了,后期假设有时间我也会解说一下Volley的应用
    我们就从Volley这个类開始吧

    (1) Volley.java

    public class Volley {
    
        //缓存文件夹
        private static final String DEFAULT_CACHE_DIR = "volley";
        //创建一个默认的请求队列。我们的请求创建好后。放入该队列就可以
        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;
            } catch (NameNotFoundException e) {
            }
    
            if (stack == null) {
    			
    			/**
    				假设我们没有传入stack,那么自己创建一个,假设sdk>9(就是2.3以上)。那么使用
    				HttpURLConnection实现http请求,假设2.3曾经使用HttpClient实现,由于在2.3曾经httpURLConnection不稳定
    			*/
                if (Build.VERSION.SDK_INT >= 9) {
                    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));
                }
            }
    		//Network类是一个网络请求类,使用stack进行网络请求
            Network network = new BasicNetwork(stack);
    		//真正创建一个请求队列,传入一个磁盘缓存和网络请求类
            RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    		//启动请求队列。事实上里面就是启动了一些线程。不断监听是否有请求
            queue.start();
    
            return queue;
        }
    
        /**
         * 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);
        }
    }


    Volley类主要用来创建一个请求队列,我们的不论什么请求(请求字符串,json,xml)都放入到这个队列中(事实上里面有两个队列,后面我们慢慢学习,临时简单理解为一个)。

    创建完队列后。调用start方法,就会启动一些线程(临时不关注多少条线程),不断监听队里里面是否有请求,假设有请求则运行http请求,在2.3之前的版本号中。Http请求是通过httpClient实现。在2.3以后的版本号中是通过HttpURLConnection实现,由于在2.3之前的版本号中HttpRlConnection非常不稳定


    (2) HttpStack.java
    以下看看HttpStack是何方神圣

    public interface HttpStack {
        /**
         名字挺吓人的,呵呵,事实上就是一个接口,它有两个实现。各自是HurlStack,HttpClientStack,通过名字大家
    	 能够猜出来一个基于HttpClient,一个基于HttpURLConnection
         * @return the HTTP response
         */
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;
    
    }

    直接查看它的子类方法吧
    首先看  HurlStack.java类
    这个类是基于HttpURLConnection实现的
    由于这个类比較长,我就重点解说一下


    (3) HurlStack.java

    /**
    	继承自HttpStack,我们临时就把Request抽象成一个请求。包括url,method等信息,后期我们会重点分析这个类
    	第二个參数就是一些请求头信息
    	*/
        @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)); } //这种方法名字非常长。事实上功能非常easy,就是为connection设置请求方法 如get post等等 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); } } //http的返回结果 return response; }


    (4) HttpClientStack.java

    /**
    	相对照上一个方法简单。相信使用过httpClient的同学一看就明确
    	*/
        @Override
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
    			//这种方法见名知意。就是创建一个HttpGet或者HttpPost
            HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
    		//增加头信息
            addHeaders(httpRequest, additionalHeaders);
            addHeaders(httpRequest, request.getHeaders());
    		//在请求之前进行准备工作,事实上是个空方法。非常想AsyncTask的onPreExecute
            onPrepareRequest(httpRequest);
            HttpParams httpParams = httpRequest.getParams();
            int timeoutMs = request.getTimeoutMs();
            // TODO: Reevaluate this connection timeout based on more wide-scale
            // data collection and possibly different for wifi vs. 3G.
            HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
            HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
    		//运行请求并返回结果
            return mClient.execute(httpRequest);
        }

    看到这里大家肯定认为这个框架也没有什么了不起嘛,和使用HttpURLConnection和HttpClient差点儿相同嘛。假设你真的这样认为那么你就大错特错了,事实上这个框架的核心在于线程的调度和缓存上面,后期我们会介绍的


    回到Volley类。我们看下一个陌生的类就是Network,事实上Network只是是个接口而已,它的实现类是BaskNetwork


    (5) BaskicNetwork.java

    从名字我们就能够看出来。这个类就是进行网络请求的。事实上他就是对HttpurlStack或者HttpClientStack的一个封装,真正实现请求的还是上面两个类。
    最核心的方法:

    @Override
        public NetworkResponse performRequest(Request<?> request) throws VolleyError {
            long requestStart = SystemClock.elapsedRealtime();
            while (true) {
                HttpResponse httpResponse = null;
                byte[] responseContents = null;
                Map<String, String> responseHeaders = new HashMap<String, String>();
                try {
                    // Gather headers.
                    Map<String, String> headers = new HashMap<String, String>();
                    addCacheHeaders(headers, request.getCacheEntry());
    				//调用mHttpStack运行http请求
                    httpResponse = mHttpStack.performRequest(request, headers);
                    StatusLine statusLine = httpResponse.getStatusLine();
                    int statusCode = statusLine.getStatusCode();
    
                    responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                    // Handle cache validation.
                    if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
    					//将返回结果封装成一个NetworkResponse
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                                request.getCacheEntry() == null ?

    null : request.getCacheEntry().data, responseHeaders, true); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) { 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); } 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); 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); } } } }


    这种方法调用了mHttpStack的同名方法,仅仅只是在mHttpStack中返回的是HttpResponse。在这里返回的是NetworkResponse。



    然后再看看本篇文章的最后一个类:
    RequestQueue.java
    我保留了一些keyword段,删除不影响理解的字段


    public class RequestQueue {
    
    	...
    
        //本地缓存队列。假设一个请求能够缓存,那么先放到这个队列中,假设本地缓存没有命中,则增加网络队列。见后面
        private final PriorityBlockingQueue<Request<?

    >> mCacheQueue = new PriorityBlockingQueue<Request<?>>(); //网络请求队列 private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?

    >>(); //默认的网络请求线程个数 默认四个,这个我们能够修改 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; //本地缓存的接口 private final Cache mCache; //这个类相信大家并不陌生 private final Network mNetwork; //由于网络请求在子线程中运行,这个对象将请求结果发送到ui线程,功能非常像Handler private final ResponseDelivery mDelivery; //网络线程数组 private NetworkDispatcher[] mDispatchers; //本地线程数组 仅仅有一条 private CacheDispatcher mCacheDispatcher; /** 创建一个请求队列 參数1:本地缓存 參数2: network 进行网络进行的包装类 參数3:网络请求线程池大小 參数4:就是一个将子线程的数据发送到ui线程的功能类,先能够不用关心 */ public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } /** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests */ public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } //启动本地和网络线程 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); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } } //关闭本地和网络线程 public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } } //相当于一个过滤器,对于apply方法返回true的Request能够从Queue中删除 public interface RequestFilter { public boolean apply(Request<?> request); } //借助上面的方法实现对没有运行的Request进行删除 public void cancelAll(RequestFilter filter) { synchronized (mCurrentRequests) { for (Request<?> request : mCurrentRequests) { if (filter.apply(request)) { request.cancel(); } } } } //取消全部的请求 public void cancelAll(final Object tag) { if (tag == null) { throw new IllegalArgumentException("Cannot cancelAll with a null tag"); } cancelAll(new RequestFilter() { @Override public boolean apply(Request<?

    > request) { return request.getTag() == tag; } }); } /** 将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"); // 假设该Request不能缓存,那么直接放入网络队列 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // 把Request放入具有同样CacheKey的链表中,假设没有同样的CacheKey的Request请求存在,则放入本地队列 synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { // 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; } } /** * 结束一个Request */ void finish(Request<?> request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won't be considered as in flight, but // that's not a problem as the cache has been primed by 'request'. mCacheQueue.addAll(waitingRequests); } } } } }


    写到这里先高一段落吧,来个小小的总结:Volley中有一个RequestQueue(包括本地队列和网络队列),就是请求队列。每个http请求都被封装成了一个Request,通过队列的add方法增加队列,假设一个Request能够缓存,那么先增加本地队列。假设不能缓存则增加网络队列



    欢迎继续阅读Volley框架浅析(二)

    欢迎留言讨论。。。

  • 相关阅读:
    CF459E Pashmak and Graph
    cf478D Red-Green Towers
    cf255C Almost Arithmetical Progression
    准备做,但是还没做的
    CF219C hoosing Capital for Treeland
    最小中间和
    【NO.9】jmeter
    【设计用例】站内信
    【有意思的BUG】客户端无厘头 已连网的场景初始化太慢 未连网的场景异常崩溃
    禅道BUG系统
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6970254.html
Copyright © 2020-2023  润新知