Vollery网络框架的源码分析

来源:互联网 发布:淘宝脱毛仪器有效果吗 编辑:程序博客网 时间:2024/06/06 00:54

在上一次我们采用了vollery网络请求的框架写了一个简单的图片加载的DemoVollery加载图片的小Demo ,如果想直接采用vollery进行网络的加载使用,可以直接使用该demo。对于想探求实现的底层原理的,我们这次就仔细的分析源码实现过程。

一、整体实现原理介绍

对于vollery的实现大致分为以下7个过程:

1、Request按照顺序添加到队列中,等待被轮循

2、Request先添加到CacheQueue中,排队等待被CacheDispatcher轮循

3、如果CacheQueue中有缓存,直接从缓存中解析出Response,通过消息处理器抛给主线程的监听器

4、如果本次请求的URL中本地没有缓存,那么就把Request添加到NetworkQueue中,排队等候被NetworkDispatcher轮循

5、Network中维护者4个NetworkDispatcher来轮循NetworkQueue

6、一旦有一个NetworkDispatcher轮循到结果就发起网络请求

7、发起请求后,将Response通过消息处理器抛给主线程的监听器

以上就是volleryqing请求的整体实现过程,下面我们进行详细讲解底层的实现过程。

二、创建RequestQueue的实例

我们在实际的开发中想要获取RequestQueue的实例,都是通过:

mQueue = Volley.newRequestQueue(getApplicationContext());

获取的(从上面的代码中我们可以看出RequestQueue采用单列设计模式),下面我们就从创建RequestQueue的实例的底层源码点击进去看看是干了什么?

 public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        File cacheDir = new File(context.getCacheDir(), "volley");// // 指定缓存文件夹,这里默认采用data/data/包名/cache/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) { // 根据当前SDK版本选择Network,2.3以下使用HttpClient,2.3以上选用HttpURLConnection,面试的重点                stack = new HurlStack();//采用httpCollection请求            } else {                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));//采用HttpClient请求            }        }        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);//构建BasicNetWork        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);// 通过DiskBasedCache和BasicNetwork构建RequestQueue实例        queue1.start(); // 开启消息队列        return queue1;    }

由上面的源码我们可以看出这里主要是完成3件事:

1、指定本地缓存的地址,默认的指定放在我们的data/data/包名/cache/volley目录下。

2、根据SDK的版本来指定不同的网络请求方式(重点)

3、通过DiskBasedCache和BasicNetwork构建RequestQueue实例

对于前面两步我们大家都比较好理解,第三步是我们探究源码的关键,Volley先构建了BasicNetwork和DiskBasedCache,然后将这两个对象作为参数传递给了RequestQueue,构建出了RequestQueue对象。紧接着,调用了RequestQueue.start()方法,开启了消息队列。我们来看下这个“RequestQueue.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) {// 构建网络线程,默认构建了4个网络线程,并依次开启这4个网络线程            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);            this.mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }

在“RequestQueue.start()”中,我们先在start之前进行了stop操作,然后先后构建了缓存线程CacheDispatcher和网络线程NetworkDispatcher,并将它们开启了。
PS:为什么CacheDispatcher和NetworkDispatcher可以直接调用start()方法?
这是由于CacheDispatcher和NetworkDispatcher都是Thread的子类

public class CacheDispatcher extends Thread {.....}
public class NetworkDispatcher extends Thread {.....}

因此,调用CacheDispatcher或者NetworkDispatcher对象的start()方法,最终关键逻辑一定是走的是run()方法。下面我们就逐一的讲解这两者中run()方法的底层的具体实现。

三、CacheDispatcher源码解析

 public void run() {        if(DEBUG) {            VolleyLog.v("start new dispatcher", new Object[0]);        }        Process.setThreadPriority(10);        this.mCache.initialize();        while(true) {// 开启了一个死循环,不停地在缓存队列中去Request            while(true) {                while(true) {                    while(true) {                        try {                            final Request e = (Request)this.mCacheQueue.take();// 从缓存队列中取到Request消息                            e.addMarker("cache-queue-take");                            if(e.isCanceled()) {// 如果这个Request被取消了,则不做任何操作                                e.finish("cache-discard-canceled");                            } else {                                Entry entry = this.mCache.get(e.getCacheKey());// 根据Request获取缓存的数据实体对象                                if(entry == null) {// 如果没有这个Request的缓存,则将这个Request加入到网络队列中                                    e.addMarker("cache-miss");                                    this.mNetworkQueue.put(e);                                } else if(!entry.isExpired()) {// 如果能取到Request的缓存,但是缓存已经过期了,也将Request加入到网络队列去                                    e.addMarker("cache-hit");                                    Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));// 能获取到缓存,将缓存中存储的网络响应的原始数据(字节数组)转换成对应的响应数据                                    e.addMarker("cache-hit-parsed");                                    if(!entry.refreshNeeded()) {                                        this.mDelivery.postResponse(e, response);// 将Request的响应数据Response分发到主线程                                    } else {                                        e.addMarker("cache-hit-refresh-needed");                                        e.setCacheEntry(entry);                                        response.intermediate = true;                                        this.mDelivery.postResponse(e, response, new Runnable() {                                            public void run() {                                                try {                                                    CacheDispatcher.this.mNetworkQueue.put(e);                                                } catch (InterruptedException var2) {                                                    ;                                                }                                            }                                        });                                    }                                } else {                                    e.addMarker("cache-hit-expired");                                    e.setCacheEntry(entry);                                    this.mNetworkQueue.put(e);                                }                            }                        } catch (InterruptedException var4) {                            if(this.mQuit) {                                return;                            }                        }                    }                }            }        }    }

总结:

1、可以看出代码中主要是开启一个死循环不停地在缓存队列中去Request

2、取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理

3、若Request没有被取消,则看是否有这个Request对应的缓存数据,若没有缓存数据,则将Request添加到网络队列中去

4、有缓存数据,则判断缓存数据是否已经过期,若数据过期,则添加Request到网络队列

5、若缓存数据没有过期,则将缓存的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String

6、将解析好的Response响应分发到主线程

那么是怎样将解析好的Response响应分发到主线程中的呢?我们点击源码查看一下:

@Overridepublic void postResponse(Request<?> request, Response<?> response, Runnable runnable) {    request.markDelivered();    request.addMarker("post-response");    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));}

而这个mResponsePoster实际上是一个线程池,它重写了自己的execute方法,在execute方法执行时,将对应的Runnable对象借助Handler分发到了主线程去执行:

ublic class ExecutorDelivery implements ResponseDelivery {    /** Used for posting responses, typically to the main thread. */    private final Executor mResponsePoster;    /**     * Creates a new response delivery interface.     * @param handler {@link Handler} to post responses on     */    public ExecutorDelivery(final Handler handler) {        // Make an Executor that just wraps the handler.        mResponsePoster = new Executor() {            @Override            public void execute(Runnable command) {                handler.post(command);            }        };    }    ……}

因此,我们的Response响应最终会在主线程被调用方接收并进行处理。

四、NetworkDispatcher源码解析

@Overridepublic void run() {    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 开启死循环,不停的去队列中取Request    while (true) {        long startTimeMs = SystemClock.elapsedRealtime();        Request<?> request;        try {            // Take a request from the queue.// 从缓存队列中取到Request消息            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.// 如果这个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);        }    }}

总结:

1、NetworkDispatcher被开启的时候,内部实际上是维护这一个while(true)死循环,不停的从队列中取Request

2、取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理

3、若Request没有被取消,则根据Request去执行网络请求,获取响应Response

4、将响应的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String

5、缓存响应的数据Response

6、将响应的信息分发到主线程

执行网络网络请求,获取Request对应的响应Response,调用的是“mNetwork.performRequest(request)”方法,实际上底层执行的是HurlStack或者HttpClientStack中的performRequest方法。若是执行HurlStack的performRequest方法,则底层其实是调用的HttpURLConnection;若是执行HttpClientStack的performRequest方法,则底层其实是调用的HttpConnection。分发Response到主线程的逻辑与CacheDispatcher中的分发逻辑完全一致。

至此,我们对于vollery框架的源码分析就结束了,希望对你有所帮助。

1 0
原创粉丝点击