• Android:Volley源代码解析


    简单实例

    Volley是一个封装HttpUrlConnection和HttpClient的网络通信框架,集AsyncHttpClient和Universal-Image-Loader的长处于了一身。既能够像AsyncHttpClient一样很easy地进行HTTP通信,也能够像Universal-Image-Loader一样轻松载入并缓存下载的图片。Volley在性能方面也进行了大幅度的调整,它的设计目标就是进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比方说下载文件等,Volley的表现就会比較糟糕。从以下这个简单的实例来研究一下源代码。

    RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
    StringRequest stringRequest = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
        @Override
        public void onResponse(String s) {
            tv.setText(s);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
    
        }
    });
    mQueue.add(stringRequest);

    流程

    1、以静态工厂形式实例化一个RequestQueue对象

    RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);

    • 首先看一下RequestQueue这个类:
    public class RequestQueue {
        private AtomicInteger mSequenceGenerator;
        private final Map<String, Queue<Request>> mWaitingRequests;
        private final Set<Request> mCurrentRequests;
        private final PriorityBlockingQueue<Request> mCacheQueue;
        private final PriorityBlockingQueue<Request> mNetworkQueue;
        private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
        private final Cache mCache;
        private final Network mNetwork;
        private final ResponseDelivery mDelivery;
        private NetworkDispatcher[] mDispatchers;
        private CacheDispatcher mCacheDispatcher;
    
        public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
            this.mSequenceGenerator = new AtomicInteger();
            this.mWaitingRequests = new HashMap();
            this.mCurrentRequests = new HashSet();
            this.mCacheQueue = new PriorityBlockingQueue();
            this.mNetworkQueue = new PriorityBlockingQueue();
            this.mCache = cache;
            this.mNetwork = network;
            this.mDispatchers = new NetworkDispatcher[threadPoolSize];
            this.mDelivery = delivery;
        }
    
        public RequestQueue(Cache cache, Network network, int threadPoolSize) {
            this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
        }
    
        public RequestQueue(Cache cache, Network network) {
            this(cache, network, 4);
        }
        ...
    }

    从构造函数可知,mWaitingRequests、mCurrentRequests、mCacheQueue、mNetworkQueue是以组合形式实例化,后两者是堵塞队列;而mCache、mNetwork是以聚合形式注入;mDelivery默认也是组合形式new ExecutorDelivery(new Handler(Looper.getMainLooper())))实例化。

    • newRequestQueue方法:
    public static RequestQueue newRequestQueue(Context context) {
            return newRequestQueue(context, (HttpStack)null);
        }
     public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
            File cacheDir = new File(context.getCacheDir(), "volley");
            String userAgent = "volley/0";
    
            try {
                String network = context.getPackageName();
                PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
                userAgent = network + "/" + queue.versionCode;
            } catch (NameNotFoundException var6) {
                ;
            }
    
            if(stack == null) {
                if(VERSION.SDK_INT >= 9) {
                    stack = new HurlStack();
                } else {
                    stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
                }
            }
    
            BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
            RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
            queue1.start();
            return queue1;
        }

    结合RequestQueue类可知,实例化的RequestQueue对象,注入了new DiskBasedCache(cacheDir)network1。缓存方式默认是磁盘缓存。NetWork对象会依据系统版本号。选用不同的Http通信方式。

    • queue.start()方法
    public void start() {
            this.stop();
            this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
            this.mCacheDispatcher.start();
    
            for(int i = 0; i < this.mDispatchers.length; ++i) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
                this.mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
    
        }

    CacheDispatcher和NetworkDispatcher都是继承Thread类。所以这种方法生成一条缓存分发线程,和四条网络线程。

    • CacheDispatcher类继承Thread类。全部參数都是聚合形式注入,看下关键的run()方法。因为代码较长。这里不贴了,分段分析下几个比較重要的方法
    while(true){
        ...
        Request e = (Request)this.mCacheQueue.take();
        ...
    }

    首先任务是一个死循环,因为mCacheQueue是个堵塞队列,所以将不断地从堵塞队列读取Request


     Entry entry = this.mCache.get(e.getCacheKey());
                                        if(entry == null) {
                                            e.addMarker("cache-miss");
                                            this.mNetworkQueue.put(e);
                                        } else if(entry.isExpired()) {
                                            e.addMarker("cache-hit-expired");
                                            e.setCacheEntry(entry);
                                            this.mNetworkQueue.put(e);
                                        } else {
                                            e.addMarker("cache-hit");
                                            Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                                            ...
                                            }

    推断请求是否有缓存。假设没有或者缓存已经过期。将请求放到网络队列里面。否则找到缓存。则进行以下的操作。


    Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
    

    parseNetworkResponse是Request类的抽象方法,我们进去StringRequest看下:

    protected Response<String> parseNetworkResponse(NetworkResponse response) {
            String parsed;
            try {
                parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            } catch (UnsupportedEncodingException var4) {
                parsed = new String(response.data);
            }
    
            return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
        }

    可看作是对网络下载的数据进行解析处理。然后返回。


    this.mDelivery.postResponse(e, response);

    最后进行这一步,mDelivery是在RequestQueue里面实例化后注入CacheDispatcher的。详细的实例化对象:new ExecutorDelivery(new Handler(Looper.getMainLooper()))。看下ExecutorDelivery类。找到postResponse方法。

        public void postResponse(Request<?> request, Response<?> response) {
            this.postResponse(request, response, (Runnable)null);
        }
    
        public void postResponse(Request<?

    > request, Response<?

    > response, Runnable runnable) { ... this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable)); }

    继续往下看

    private class ResponseDeliveryRunnable implements Runnable {
        ...
        run(){
            ...
            if(this.mResponse.isSuccess()) {
                        this.mRequest.deliverResponse(this.mResponse.result);
                    } 
            ...
        }
        ...
    }

    deliverResponse方法相同是Request类的抽象方法,我们进去StringRequest看下

        protected void deliverResponse(String response) {
            this.mListener.onResponse(response);
        }
    

    就一句回调


    • NetworkDispatcher类相同继承Thread类,其分析过程和CacheDispatcher差点儿相同,重要的相同是以下几步:

    1、从网络堵塞队列读取请求,request = (Request)this.mQueue.take();
    2、网络下载,NetworkResponse e = this.mNetwork.performRequest(request);(假设是CacheDispatcher这一步就是缓存推断)
    3、处理下载后的数据,Response response = request.parseNetworkResponse(e);
    3、对处理后的数据进行回调,this.mDelivery.postResponse(e, response)。


    2、实例化一个Request对象

    StringRequest stringRequest = new StringRequest (url,listener,errorListener);

    public class StringRequest extends Request<String> {
        private final Listener<String> mListener;
    
        public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
            super(method, url, errorListener);
            this.mListener = listener;
        }
    
        public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
            this(0, url, listener, errorListener);
        }
    
        protected void deliverResponse(String response) {
            this.mListener.onResponse(response);
        }
    
        protected Response<String> parseNetworkResponse(NetworkResponse response) {
            String parsed;
            try {
                parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            } catch (UnsupportedEncodingException var4) {
                parsed = new String(response.data);
            }
    
            return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
        }
    }

    由第一个分析步骤能够知道,这个Request主要就是进行两个操作,也就是重写两个方法。

    1. protected abstract Response<T> parseNetworkResponse(NetworkResponse var1);对下载后的数据进行解析处理。
    2. protected abstract void deliverResponse(T var1);最后回调操作这个数据的方法。

    所以构造函数仅需下载地址和回调操作的方法。


    3、调用queue.add()方法

     if(!request.shouldCache()) {
                this.mNetworkQueue.add(request);
                return request;
            } 

    假设不须要缓存就直接加入到网络队列里面,Request有个比較重要的布尔字段mShouldCache,默认是用来推断是否要进行磁盘缓存的。


    this.mCacheQueue.add(request);

    否则将其加入到缓存队列,这种方法上面也会进行一些当前队列和等待队列的防反复的操作。


    小结

    这里写图片描写叙述

    框架部分:
    1、实例化一个RequestQueue对象,开启一条缓存线程和默认的四条网络线程,线程不断地从缓存堵塞队列和网络堵塞队列里面读取请求;
    2、假设缓存线程从缓存队列里面读取的请求已经缓存过,则解析数据回调操作方法。否则将其加入到网络队列;
    3、假设缓存线程从缓存队列里面读取的请求没有缓存过,则加入到网络队列。
    4、网络线程从网络堵塞队列不断读取请求,读到请求后则由封装好的HttpStack对象进行网络下载处理、下载后回调对数据处理的方法,处理后回调操作数据的方法。

    客户部分:
    1、实例化一个请求对象。在请求对象里面重写处理网络下载后的数据的方法。和操作处理后的数据的方法。
    2、将请求对象加入到请求队列。请求须要缓存则会被加入到分配到缓存队列,不须要则被加入到网络队列。


    之前看过一个问题。说框架和库有什么不同,高人答曰:框架是他调用你代码,库是你调用他代码。

    优秀的框架拓展性是如此之强,尽管自己远没那个能力,只是也算开了眼界!

  • 相关阅读:
    Java经典习题7
    Java经典习题6
    java经典习题5
    前后端分离开发——模拟数据mock.js
    微信网页第三方登录原理
    TP5常量
    TP5
    健忘的正则
    JS正则
    apache配置修改
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/7299071.html
Copyright © 2020-2023  润新知