volley源码分析

    xiaoxiao2023-03-24  3

             下图为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()  为一个抽象方法 , 解析数据逻辑需要自己重写去实现 )

             

    转载请注明原文地址: https://ju.6miu.com/read-1201107.html
    最新回复(0)