Android网络框架-Volley(二) RequestQueue源码分析以及建立一个RequestQueue

从上一篇文章我们可以知道,Volley的整个工作都是建立在一个RequestQueue上的,所以理解RequestQueue对于我们使用Volley是很有必要的,上一篇文章的例子中,我们使用Volley.newRequestQueue()创建了一个RequestQueue实例,那么我们就先来看一看Volley是怎么把RequestQueue创建出来的吧。

public class Volley {

    /** 默认的cache数据保存在data/data/包名/volley/cache中 */
    private static final String DEFAULT_CACHE_DIR = "volley";

    /**
     * 创建一个默认的RequestQueue实例
     * 创建一个RequestQueue需要两个必不可少的东西
     * 1.一个network   2.一个cache
     * network用于发送网络请求
     * cache用于存储缓存
     *
     * @param context 用来创建cache目录的.
     * @return 返回一个RequestQueue实例.
     */
    public static RequestQueue newRequestQueue(Context context) {
        //部分代码省略,只看核心代码
        。。。
        
        
        HttpStack stack;
        if (Build.VERSION.SDK_INT >= 9) {
        //判断当前设备的版本信息,如果API大于等于9的话,就使用HttpURLConnection建立网络连接。
            stack = new HurlStack();
        } else {
            // 在API小于9的版本中,HttpURLConnection是存在bug的,所以使用AndroidHttpClient建立连接
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
        
        //BasicNetwork是Volley中默认的Network实现类,他通过传进来的stack来判断是用AndroidHttpClient
        //还是HttpURLConnection来创建一个network,这是RequestQueue的第一个必需品
        Network network = new BasicNetwork(stack);
        //new DiskBasedCache()新建了一个cache实例,这是RequestQueue第二个必需品
        //这两个必需品都准备完了,作为RequestQueue的参数,可以新建一个RequestQueue实例
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        //注意,在调用Volley。newRequestQueue()的时候,就已经启动了请求队列,请求队列也是一个线程。后面会分析到
        queue.start();

        return queue;
    }
}

通过分析Volley类的源码,我们可以发现Volley在创建网络连接的时候是非常讲究的,我们想想自己原来在建立网络连接的时候,有没有考虑过HttpClient选择的问题。毕竟Volley是Google工程师开发出的框架,他们对自己的产品最熟悉. 我们再来看一下RequestQueue的源码

public class RequestQueue {
   
    //用来生成单调递增的TAG赋给request
    private AtomicInteger mSequenceGenerator = new AtomicInteger();
    
    //RequestQueue的第一个必需品
    private final Network mNetwork;

    //RequestQueue的第二个必需品
    private final Cache mCache;
    
    //ResponseDelivery接口,用于将结果交付给主线程
    private final ResponseDelivery mDelivery;

    //网络管家
    private NetworkDispatcher[] mDispatchers;

    //缓存管家
    private CacheDispatcher mCacheDispatcher;
    
    //这个是存储request的mCurrentRequests,这是一个set,set不允许有重复的数据,
    //那么mCurrentRequsts中的request都是唯一的,如果有重复的request怎么办,看下面
    private final Set mCurrentRequests = new HashSet();
    
    //mWaitingRequests就是用来存储重复request的,将具有相同url地址的request列成
    //一队作为值,然后url作为键,加入到这个map中
    private final Map> mWaitingRequests =
            new HashMap>();
    
    //缓存队列        
    private final PriorityBlockingQueue mCacheQueue =
        new PriorityBlockingQueue();
    
    //网络队列    
    private final PriorityBlockingQueue mNetworkQueue =
        new PriorityBlockingQueue();
    
    //默认的网络线程池大小为4    
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    
   

    /**
     * 在Volley.newRequestQueue()中,调用了queue的start()方法,即此方法,这个方法
     * 主要是把CacheDispatcher和NetworkDispatcher启动起来,即调用他们的start()方法
     */
    public void start() {
        //首先先确保当前没有正在运行的dispatcher,如果有的话,就停止掉
        stop(); 
        // 创建一个CacheDispatcher实例
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        //启动CacheDispatcher线程,即启动缓存线程
        mCacheDispatcher.start();

        //根据线程池数量新建NetworkDispatcher实例(默认为4个)
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            //启动NetworkDispatcher,即启动网络线程
            networkDispatcher.start();
        }
    }

    /**
     * 停止所有的dispatchers
     */
    public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }


    /**
     * 在上一篇文章的例子中,我们最后一步就是把request加入到requestQuue中,即调用此方法
     */
    public Request add(Request request) {
        //给当前的request一个标识,表示他是属于这个requestQueue的,其他的
        //requestQueue别管。
        request.setRequestQueue(this);
        //将当前request添加到mCurrentRequests中,这里面的request都是唯一的
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        //给request赋值一个单调递增的TAG,来标识他们
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // 如果这个request是不可缓存的,那么就跳过缓存队列,直接添加到网络队列中
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // 在这里处理重复的request
        synchronized (mWaitingRequests) {
            //能走到这一步,说明这个request是可缓存的,我们可以通过
            //调用getCacheKey()方法,getCacheKey()调用getUrl()方法
            //最后其实就是得到的这个request的url地址
            String cacheKey = request.getCacheKey();
            //检查一下在当前的等待队列中有没有此url
            if (mWaitingRequests.containsKey(cacheKey)) {
                // 如果有的话,获得具有相同url地址的request,将他们列队
                Queue stagedRequests = mWaitingRequests.get(cacheKey);
                //如果这个队列为空的话,说明缓存过这个url地址,但是没有request,
                //当前request是第一个,那就需要新建一个链表
                if (stagedRequests == null) {
                    //新建一个链表
                    stagedRequests = new LinkedList();
                }
                //将此request加入到链表中
                stagedRequests.add(request);
                //url地址作为键,装有request的链表作为值
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                //没有缓存过这个url,则将url地址作为键,null作为值,下一次再有
                //这个url地址的request出现时,可以进行if里面的步骤
                mWaitingRequests.put(cacheKey, null);
                //将这个request加入到缓存队列中
                mCacheQueue.add(request);
            }
            return request;
        }
    }
}

总结: 1.在调用Volley.newInstance()之后,就会调用到RequestQueue中的start()方法,RequestQueue中start()方法会调用CacheDispatcher和NetworkDispatcher的start()方法,将缓存管家和网络管家启动起来。 2.在调用add()方法后,一个request就传进来了,给他设置两个标识,一个是它属于这个requestQueue处理,另一个是一个单调递增的TAG。 3.将这个request添加到mCurrentRequests中,这是一个set,如果mCurrentRequests中已经有了这个request,那么它自然是加不进去的,作为重复的request来处理 4.处理重复request。获得这个request的url地址     4.1.如果这个url地址之前被缓存过并且存在存储这个相同request的一个链表,那么就将这个request添加到这个链表中,否则新建一个链表。     4.2.如果这个url地址之前没被缓存过,那么就将url作为键,null作为值插入到这个map中。下一次的时候,就会进行4.1了。 我们可以在项目中的任何地方新建一个RequestQueue,再新建一个Request,add到RequestQueue中后就可以工作了,但是如果我们项目中很多地方都要用到网络请求,那么在很多地方都新建一个RequestQueue肯定是不好的,所以,在使用Volley的时候,我们把RequestQueue设置为全局单例的。

public class AppController extends Application {

    public static final String TAG = AppController.class.getSimpleName();
    //
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    private static AppController mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized AppController getInstance() {

        return mInstance;
    }
    //获得requestQueue实例,如果没有的话则创建一个新的
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            //在这里调用Volley.newRequestQueue()
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }
    //获得imageLoader实例,因为我们会加载图片,这个是Volley提供的图片加载器
    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            //新建imageLoader需要传入两个参数,第一个为当前的requestQueue
            //第二个为设置缓存的大小,LruBitmapCache对缓存做了初始化,后面会介绍
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }
    //将Request添加到requestQueue中的方法,可以给request设置一个tag作为标记
    public  void addToRequestQueue(Request req, String tag) {
        // 如果没有传入tag,那么就将TAG传入作为默认的标记
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }
    //将TAG作为request的默认标记
    public  void addToRequestQueue(Request req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }
    //取消队列中的标记为tag的request
    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }


}
public class LruBitmapCache implements ImageLoader.ImageCache {
    private android.util.LruCache mCache;
    //新建一个LruCache,这是个一级缓存,是缓存到内存上的
    public LruBitmapCache() {
        //指定大小为15Mb
        int maxSize = 15 * 1024 * 1024;
        mCache = new android.util.LruCache(maxSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }

        };
    }
    //ImageLaoder.ImageCache借口的回调方法,从内存缓存中获取图片
    @Override
    public Bitmap getBitmap(String url) {
        return mCache.get(url);
    }
    //将图片缓存到内存中
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        mCache.put(url, bitmap);
    }

}

这样,我们的标准RequestQueue全局单例实例就已经创建好了,我们可以在需要访问网络的地方直接调用AppController.getInstance().addToRequestQueue()就可以开始工作了。 最后在清单文件中将AppController注册进去并且添加网络权限



    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/nugongahou110/article/details/46831233
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞