Volley框架(五):Volley源码分析

本文主要分析Volley的执行过程,包括Volley、Request、RequestQueue、CacheDispatcher、NetworkDispatcher、ResponseDelivery等关键类的作用。Volley对外提供API,通过newRequestQueue启动请求队列。CacheDispatcher处理缓存请求,NetworkDispatcher处理网络请求,ResponseDelivery分发结果。文章还详细介绍了Volley的工作流程及各个组件之间的交互。
摘要由CSDN通过智能技术生成

在前面几篇博客中我们已经把Volley的一些常见用法基本上说得差不多了。说了Volley那么多的用法,但是对于他的原理还不是很了解,所以这篇博客主要来分析一下Volley的执行过程。

首先,我们看一下Volley中的几个主要类:

Volley:Volley 对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。
Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest 都是它的子类,表示某种类型的请求。
RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。
CacheDispatcher:一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher去调度处理。
NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
ResponseDelivery:返回结果分发接口,只有一个子类ExecutorDelivery实现对结果的分发。
HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient的HttpClientStack(可以进行扩展,比如使用okHttp进行网络请求,可以参照HurlStack和HttpClient的实现来自定义)。
Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。

我们看一张Volley执行流程图:



接下来,我们就从代码上分析Volley执行流程图:

1.创建一个请求队列,Volley类中的静态方法:

public static RequestQueue newRequestQueue(Context context)
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes)
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes)
参数说明:
Context:上下文
HttpStack:处理 Http 请求,返回请求结果
maxDiskCacheBytes:指定DiskBasedCache(磁盘缓存)的最大字节数

Volley中newRequestQueue()方法具体做的事情:

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
    // 定义缓存目录,保存在应用程序的缓存目录下
    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) {
    }
    // 如果没有传入stack,那么就根据sdk版本进行判断使用HurlStack还是使用HttpClientStack
    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }
    //  初始化一个BasicNetwork并传入一个stack
    Network network = new BasicNetwork(stack);

    // 创建RequestQueue,并绑定一个DiskBasedCache对象
    RequestQueue queue; 
    if (maxDiskCacheBytes <= -1){
        // 没有指定最大磁盘缓存
        queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    } else{
        // 指定了最大的磁盘缓存
        queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }
    // 调用RequestQueue类的start()方法
    queue.start();
    return queue;
}
RequestQueue类的3个构造方法:
// mDispatchers:网络线程(NetworkDispatcher)数组,默认4个
// mDelivery:ResponseDelivery对象,使用的是他的实现类 ExecutorDelivery
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) {
    // 创建一个ExecutorDelivery对象,并将主线程的Looper对象作为参数传递
    this(cache, network, threadPoolSize,
            new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

public RequestQueue(Cache cache, Network network) {
    // DEFAULT_NETWORK_THREAD_POOL_SIZE 指定网络调度线程的个数,默认为4
    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
我们通过Volley.newRequestQueue(...)的方式创建了RequestQueue的时候,在Volley内部实际上是调用的RequestQueue的构造方法创建了RequestQueue对象,同时初始化了一个BasicNetwork对象和绑定了一个DiskBasedCache对象,然后调用了RequestQueue的start()方法,最后将RequestQueue对象返回。当我们调用RequestQueue对象的add()方法的时候就会将Request对象加入到队列中。

我们看一下RequestQueue类中的add()方法:

public <T> Request<T> add(Request<T> request) {
	// 将Request与当前队列绑定在一起
	request.setRequestQueue(this);
	// 将Request加入到mCurrentRequests中,同步是因为有多个线程
	synchronized (mCurrentRequests) {
		mCurrentRequests.add(request);
	}

	// 为当前的Request设置一个序列号
	request.setSequence(getSequenceNumber());
	request.addMarker("add-to-queue");

	// 如果request指定了不要缓存,那么直接将该Request增加到网络调度线程
	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) {
		// 获取该request的缓存键名
		String cacheKey = request.getCacheKey();
		// 判断正在等待发送网络请求的队列中是否包含该请求
		if (mWaitingRequests.containsKey(cacheKey)) {
			// 如果包含了,那么等待
			Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
			if (stagedRequests == null) {
				stagedRequests = new LinkedList<Request<?>>();
			}
			// 将当前的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 {
			// 如果没有包含相同的请求,那么将等待网络请求队列中的当前缓存键名的值设置为null
			mWaitingRequests.put(cacheKey, null);
			// 然后将request添加到缓存队列
			mCacheQueue.add(request);
		}
		// 将传入的request返回
		return request;
	}
}

接下来我们看一下RequestQueue类的start()方法:

public void start() {
    stop();  // 停止正在运行的dispatcher
    // 创建一个处理缓存的调度线程并开启(执行run方法)
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    mCacheDispatcher.start();

    //创建指定个数的网络请求调度线程并开启(执行run方法)
    for (int i = 0; i < mDispatchers.length; i++) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}
RequestQueue类的start()方法主要是创建了一个缓存调度线程和多个网络调度线程并让他们开始执行。

缓存调度线程CacheDispatcher类的run()方法:
public void run() {
    ...// 初始化操作

    Request<?> request;
    while (true) { // 开启循环,不断的从队列中取值
        request = null;
        try {
            // 从缓存队列中获取Request对象
            request = mCacheQueue.take();
        } catch (InterruptedException e) {
            // 发生异常并且需要结束就退出整个循环,否则就跳出此次循环
            if (mQuit) {
                return;
            }
            continue;
        }
        try {
            // 增加一个标记
            request.addMarker("cache-queue-take");
            // 判断请求是否取消,如果取消,跳出此次循环
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // 通过缓存的键名获取缓存(Entry)
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                // 没有缓存,加入到网络请求队列,跳出此次循环
                request.addMarker("cache-miss");
                mNetworkQueue.put(request);
                continue;
            }

            if (entry.isExpired()) {
                // 如果缓存已经过期,加入到网络请求队列,跳出此次循环
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                mNetworkQueue.put(request);
                continue;
            }

            // 有缓存并且没有过期,调用Response的parseNetworkResponse()方法,解析数据
            request.addMarker("cache-hit");
            Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
            request.addMarker("cache-hit-parsed");

            // 判断是否需要刷新缓存
            if (!entry.refreshNeeded()) {
                // 不需要刷新缓存,直接调用ResponseDelivery的方法分发结果
                mDelivery.postResponse(request, response);
            } else {
                // 需要刷新缓存,重新设置缓存
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);

                // Mark the response as intermediate.
                response.intermediate = true;

                // 将数据进行分发,然后加入到网络请求队列
                final Request<?> finalRequest = request;
                mDelivery.postResponse(request, response, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mNetworkQueue.put(finalRequest);
                        } catch (InterruptedException e) {
                        }
                    }
                });
            }
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
        }
    }
}
网络调度线程NetworkDispatcher类的run()方法:
public void run() {
    ...
    Request<?> request;
    while (true) {
        ...
        request = null;
        try {
            // 从队列中获取Request对象
            request = mQueue.take();
        } catch (InterruptedException e) {
            // 发生异常并且需要结束就退出整个循环,否则跳出此次循环
            if (mQuit) {
                return;
            }
            continue;
        }

        try {
            request.addMarker("network-queue-take");
            // 判断请求是否取消
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                continue;
            }
            addTrafficStatsTag(request);
            // 执行网络请求得到响应
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");

            // 过滤重复请求和处理304返回码
            if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                request.finish("not-modified");
                continue;
            }

            // 在网络线程中解析数据(通过Request的parseNetworkResponse()方法)
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");

            // 根据shouldCache()/是否需要缓存方法设置缓存
            if (request.shouldCache() && response.cacheEntry != null) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }
            request.markDelivered();
            // 调用ResponseDelivery的方法分发结果
            mDelivery.postResponse(request, response);
        } catch (VolleyError volleyError) {
        } catch (Exception e) {
            ...
           // 调用ResponseDelivery的方法分发错误结果
           mDelivery.postError(request, error);
        }
    }
}
不管是缓存线程还是网络线程获取到数据之后都是通过ResponseDelivery中的方法来分发数据,那么我们就来看一下ResponseDelivery这个类。通过源码我们看到ResponseDelivery是一个接口, 所以查看实现类ExecutorDelivery的方法:
// 构造方法:初始化了一个mResponsePoster对象
public ExecutorDelivery(final Handler handler) {
    // 当调用mResponsePoster的execute()方法时,将command交给handler的post方法
    // 这里的Handler对象是在创建 RequestQueue时传递的,也就是new Handler(Looper.getMainLooper()),绑定的主线程的Looper对象
    // 所以这里将command传递到主线程中
    mResponsePoster = new Executor() {
        @Override
        public void execute(Runnable command) {
            handler.post(command);
        }
    };
}

// 解析响应,并且通过Handler机制将响应的结果从缓存线程或者网络线程中传递到主线程
public void postResponse(Request<?> request, Response<?> response) {
    // 调用重载的postResponse()方法
    postResponse(request, response, null);
}

// 解析响应,并且通过Handler机制将响应的结果从缓存线程或者网络线程中传递到主线程
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
    request.markDelivered();
    request.addMarker("post-response");
    // 通过mResponsePoster对象执行ResponseDeliveryRunnable线程的run方法
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

//发生错误时,将错误消息传递到主线程
public void postError(Request<?> request, VolleyError error) {
    request.addMarker("post-error");
    Response<?> response = Response.error(error);
    // 通过mResponsePoster对象执行ResponseDeliveryRunnable线程的run方法
    mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
ExecutorDelivery类在分发结果时,都是通过mResponsePoster的execute()方法执行一个ResponseDeliveryRunnable线程, ResponseDeliveryRunnable线程类是ExecutorDelivery的一个内部类,我们直接查看他的run()方法:
public void run() {
    // 请求取消,直接返回
    if (mRequest.isCanceled()) {
        mRequest.finish("canceled-at-delivery");
        return;
    }

    // 请求成功,调用Request的deliverResponse()方法
    if (mResponse.isSuccess()) {
        mRequest.deliverResponse(mResponse.result);
    } else {
        // 请求错误,调用Request的deliverError()方法
        mRequest.deliverError(mResponse.error);
    }
    ...
}
在run()方法中首先判断是否该请求已经取消,接着调用Request的方法判断是否成功,然后根据是否成功执行相应回调。
Request的deliverResponse()方法和deliverError()方法定义在Request类中
// 抽象方法,子类实现
abstract protected void deliverResponse(T response);
// 子类不需要再实现,当我们在创建Request对象的时候传递了ErrorListener监听,就执行回调
public void deliverError(VolleyError error) {
    if (mErrorListener != null) {
        mErrorListener.onErrorResponse(error);
    }
}


我们来查看一下Request的一个子类StringRequest类的实现:
public class StringRequest extends Request<String> {
    private Listener<String> mListener;
    // 需要传递Listener(正确监听)和ErrorListener(错误监听)
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void onFinish() {
        super.onFinish();
        mListener = null;
    }

    // 通过Listener进行回调(在主线程)
    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

    // 解析响应数据(非主线程)
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
}
我们是这样创建StringRequest的请求对象的
StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    }
});

RequestQueue中的其他方法:
public void stop() {
	if (mCacheDispatcher != null) {
		mCacheDispatcher.quit();
	}
	for (int i = 0; i < mDispatchers.length; i++) {
		if (mDispatchers[i] != null) {
			mDispatchers[i].quit();
		}
	}
}
// 通过调用缓存调度线程和网络调度线程的quit()方法结束线程,者两个类中的quit()方法做了同样的事,
public void quit() {
	mQuit = true;
	interrupt();
}

将mQuit变量的值设置为true,那么当缓存调度线程和网络调度线程调用take()方法时就会发生异常,然后又判断了mQuit变量,当mQuit变量为true时,结束循环,也就是结束了线程。


finish()方法:

void finish(Request<?> request) {
	// 从mCurrentRequests集合中remove掉当前Request
	synchronized (mCurrentRequests) {
		mCurrentRequests.remove(request);
	}

	// 如果需要缓存,继续操作
	if (request.shouldCache()) {
		synchronized (mWaitingRequests) {
			String cacheKey = request.getCacheKey();
			// 从mWaitingRequests(等待请求的集合)中去掉当前的Request
			Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
			if (waitingRequests != null) {
				if (VolleyLog.DEBUG) {
					VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
							waitingRequests.size(), cacheKey);
				}
				// 然后将与当前request有相同的cacheKey的集合全部增加到缓存队列中,通过缓存调度线程进行处理
				mCacheQueue.addAll(waitingRequests);
			}
		}
	}
}

取消请求方法:
// 通过滤波器取消当前队列中的相关请求
public void cancelAll(RequestFilter filter) {
	synchronized (mCurrentRequests) {
		for (Request<?> request : mCurrentRequests) {
			if (filter.apply(request)) {
				request.cancel();
			}
		}
	}
}

// 取消所有具有相同tag的请求(tag不能为null)
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;
		}
	});
}

到这里,关于Volley的一些内容就完成了。


前文链接:

Volley框架(一):使用Volley请求数据
Volley框架(二):使用Volley加载图片
Volley框架(三):使用Volley提交表单数据
Volley框架(四):使用Volley上传文件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值