volley源码分析


         下图为Volley执行的简单流程的步骤


     这里以JsonRequest(即网络请求数据为json格式)为例: (先示例一段代码, 可以边看代码边分析)

1. 请求队列(缓存请求队列和网络请求队列)的初始化和启动 : 

public class VolleyApplication extends Application{

	@Override
	public void onCreate() {
		super.onCreate();
		
		RequestTest.init(getApplicationContext());
	}
}

===============================

public class RequestTest {

	private static RequestQueue mRequestQueue;

	public static void init(Context context) {
		mRequestQueue = Volley.newRequestQueue(context);
		
	}
	
	/**
	 * 发生请求 
	 * @param request
	 */
	public static void sendRequest(Request<?> request) {
		mRequestQueue.add(request);
<span style="white-space:pre">	</span>   // 将请求添加到队列中,代码中先判断请求是否需要缓存,需要则将请求添加到缓存请求队列中
<span style="white-space:pre">	</span>   //  不需要则将请求添加到网络请求队列中
	}
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

2 创建对应类继承JsonRequest : (传入对应的请求参数 , 并重写解析方法 对网络数据进行解析返回)

public class VolleyRequest<T> extends JsonRequest<T> {

	private Gson mGson;
	
	private Class<T> mClass;//T表示你想要解析的java bean的类

	/**
	 * 
	 * @param method 请求方法 GET POST (PUT DELETE),假设目前只有获取数据(只有GET可以直接写)
	 * @param url 网络请求地址
	 * @param requestBody 上传的数据
	 * @param listener 网络请求成功返回回调, 能够获取到解析后的结果
	 * @param errorListener 网络请求失败的回调
	 */
	public Volley<span style="font-family: Arial, Helvetica, sans-serif;">Request(Class<T> classz, String url, Listener<T> listener, ErrorListener errorListener) {</span>
		super(Method.GET, url, null, listener, errorListener);
		mGson = new Gson();
		mClass = classz;
	}

	/**
	 * 解析网络请求的结果,回调listener,返回解析解析后的结果,前提是网络已经成功返回
	 * 在子线程解析 , 后边将解析结果分发给主线程
	 */
	@Override
	protected Response parseNetworkResponse(NetworkResponse response) {
		//将网络返回字节数组转成字符串
		try {
			String result= new String(response.data, PROTOCOL_CHARSET);
		
			T resultBean = mGson.fromJson(result, mClass);

			//从网络请求返回的响应头里面解析出缓存相关的数据,比如缓存过期时间
			Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
			//返回解析后的结果,构成一个Response
			return Response.success(resultBean, cacheEntry);
			
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}
3,   将请求添加到请求队列中

public class RequestDatas {

	public void loadDatas(){
		
		String url = "...";
		VolleyRequest<TestBean> request = new VolleyRequest<TestBean>(TestBean.class<span style="font-family: Arial, Helvetica, sans-serif;">, url, </span>
				successListener, errorListener);
		
		RequestTest.sendRequest(request);     // 即调用  <span style="font-family: Arial, Helvetica, sans-serif;">mRequestQueue.add(request);</span>

	}
	//  回调已经被分发在主线程中执行
	private Listener<TestBean> successListener = new Listener<TestBean>(){

		@Override
		public void onResponse(TestBean bean) {
			//  成功的回调 
			
		}
		
	};
	
	private ErrorListener errorListener = new ErrorListener() {

		@Override
		public void onErrorResponse(VolleyError error) {
		    //  失败的回调
			
		}
		
	};
}

    以下来通过源码分析每个步骤的具体实现:     

               从代码最初的初始化一个请求队列开始, 由于一个app中请求数据的频繁, 可以将初始化请求队列的代码写在application中,表示当应用一开始创建就初始化好;

       RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext);   //  这一行代码里面分别执行了1和2两个步骤

1  请求队列的初始化 : 

            具体分析 :  (1) Volley.newRequestQueue(context)方法中调用了 : 

                                          newRequestQueue(context, null);  指的是 ==> newRequestQueue(Context context, HttpStack stack)  中又用了

                                          newRequestQueue(context, stack, -1);指的是 ==> newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes)

其中参数 stack 指的是栈,默认为null, 参数maxDiskCacheBytes指的是最大磁盘缓存字节大小,默认为-1;

                              在 newRequestQueue(.....)方法中会有两个判断: if (stack == null)  和  if (maxDiskCacheBytes <= -1) 作为第一次初始化栈和磁盘缓存大小的条件

             a . 初始化栈的请求方式:

 SDK版本在9或以上里面封装的是HttpUrlConnextion比较常用的也比较轻量

                 SDK版本在9以前用的是阿帕奇下的第三方请求方式 :里面封装的是 httpclient,因为9以上的HttpUrlConnextion并不稳定(可能会出BUG)

if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

接下来是创建了一个network对象 ==> Network network = new BasicNetwork(stack);

并把这个stack传给了一个网络工作的对象

          b . 初始化了磁盘缓存的对象,里面定义了缓存的路径和大小 : 路径就是data/data/包名/cache/volley,  默认大小是5M

          c . 最后是正式的创建一个请求队列的对象, 并把netWork对象和缓存对象传给他

queue = new RequestQueue(new DiskBasedCache(cacheDir), network);方法里调用了this.RequestQueue(....),

最后是调用了RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)又传入了两个参数 : 

线程池大小threadPoolSize (默认为开启四个线程NetworkDispatcher)  ====> 定义了在线程池中执行网络请求,线程池中可以开启四个子线程

mDispatchers = new NetworkDispatcher[threadPoolSize]; //threadPoolSize = 4;

响应的分发器 delivery ===> 定义了将网络请求的结果处理分发到主线程去处理

delivery =new ExecutorDelivery(new Handler(Looper.getMainLooper())); { handle.post() };

2 请求队列的启动 ( mCacheQueue缓存的请求队列,mNetworkQueue网络请求队列)

由代码 queue.start(); 开始: // 这里面总共开启了五个子线程 , 一个为获取缓存数据线程 , 四个为获取网络数据线程

a. 创建缓存的分发器CacheDispather并且启动它
     mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
     mCacheDispatcher.start();

这里开启了一个缓存线程, 调用了其中的run()方法, 方法中 :

(1) . mCache.initialize(); // 磁盘缓存的初始化  读出所有缓存的文件的头(包括key判断是否有缓存数据,ttl记录缓存过期时间等等),

并没有读出数据(当网络请求有缓存,再去读取数据),将CacheHeader存入头的集合

private final Map<String, CacheHeader> mEntries =  new LinkedHashMap<String, CacheHeader>(16, .75f, true);

(2) . while (true) { ... } // 这里是一个死循环 , 判断缓存队列 mCacheQueue 中有没有请求, 有的话将其取出, 查询一下有没有缓存
         Cache.Entry entry = mCache.get(request.getCacheKey()); 

如果entry不为null,有缓存,并且缓存没有过期,就解析缓存网络请求的响应

 Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders));  

并将解析后的结果分发到主线程中 : 

mDelivery.postResponse(request, response); 

如果缓存没有或者缓存已经过期 , 就将请求分发给网络请求队列

mNetworkQueue.put(request);
b. 网络请求线程池的网络线程的创建,创建4个NetworkDispather
     NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
networkDispatcher.start(); // 并且启动这四个线程

                            在其中的run()方法中: 

while (true) { ... } // 这里也是一个死循环 , 判断网络请求队列 mNetworkQueue 中有没有请求, 有的话则执行网络请求  

NetworkResponse networkResponse = mNetwork.performRequest(request);

Response<?> response = request.parseNetworkResponse(networkResponse);

同时将网络请求的结果做缓存处理 : 

mCache.put(request.getCacheKey(), response.cacheEntry);

                                                        最后同样将解析后的结果分发到主线程中

mDelivery.postResponse(request, response);

(注 : 方法parseNetworkResponse()  为一个抽象方法 , 解析数据逻辑需要自己重写去实现 )



         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值