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注册进去并且添加网络权限

    
    
    
     
     

    
     
     
        
      
      
            
       
       
                
        
        

                
        
        
            
       
       
        
      
      
    
     
     


    
    





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值