面试题之---Volley源码解析

本文详细介绍了Volley网络库的基本使用方法,包括依赖添加、请求队列创建、请求创建及添加流程。同时深入剖析了Volley的源码实现,如RequestQueue的创建过程、缓存机制、线程管理及请求调度机制。此外还列举了Volley的一些不足之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)基本使用
1,添加依赖
    compile 'com.mcxiaoke.volley:library:1.0.19'
2,创建请求队列
     RequestQueue queue=Volley.newRequestQueue(this);
3,创建请求
      JsonObjectRequest request=new JsonObjectRequest(url, new Response.Listener<JSONObject>() {  
                @Override  
                public void onResponse(JSONObject jsonObject) {  
                    Log.e("success",jsonObject.toString());  
                }  
            }, new Response.ErrorListener() {  
                @Override  
                public void onErrorResponse(VolleyError volleyError) {  
  
                }  
            });  
4,将请求添加到请请队列
     queue.add(request);    
- 5,volley的缺点
       5.1,在BasicNetwork中判断了statusCode(statusCode < 200 || statusCode > 299),如何符合条件直接抛出IOException(),不够合理,导致401等其他状态抛出IOException 
       5.2,不适合获取数据量大的文件
       5.3,线程没有很好的管理,4个网络线程只是放入到一个数组里面
       5.4,现在都用retrofit或者OKHttputils,特别是retrofit,自带json解析

 (二)源码解析
1,在创建RequestQueue时,会判断,如果安卓版本大于2.3,就调用HttpUrlConnection的HurlStack,否则调用HttpClient的HttpClientStack,


 
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) {
    }

    /**
     * 根据不同的系统版本号实例化不同的请求类,如果版本号小于9,用HttpClient
     * 如果版本号大于9,用HttpUrlConnection
     */
    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {

            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }
    //把实例化的stack传递进BasicNetwork,实例化Network
    Network network = new BasicNetwork(stack);

    RequestQueue queue;
    if (maxDiskCacheBytes <= -1)
    {
        //实例化RequestQueue类
        queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else
    {
        //调用RequestQueue的start()方法
        queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }

    queue.start();

    return queue;
}



2,可以创建各种请求,比如StringRequest,JsonRequest,ImageRequest等等

 

3在调用add()方法将request添加到请求队列RequesuQueue时,通过request.shouldCache()来判断是否可以缓存,不能缓存就将请求添加到网络请求队列中;如果能缓存,就判断之前是否有执行相同的请求且还没有结果返回的,如果有的话,就将此请求加入waitingRequesu队列,不再重复请求,没有的话就将此请求加入缓存队列 .

public <T> Request<T> add(Request<T> request) {
 //标记当前请求,表示这个请求由当前RequestQueue处理 
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    // /获得当前请求的序号 
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");
    //如果请求不能缓存,直接添加到网络请求队列,默认是可以缓存 
    if (!request.shouldCache()) {
        mNetworkQueue.add(request);
        return request;
    }
    // 锁定当前代码块,只能一条线程执行 
    synchronized (mWaitingRequests) {
        String cacheKey = request.getCacheKey();
        //是否有相同请求正在处理,
        if (mWaitingRequests.containsKey(cacheKey)) {
            Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
            if (stagedRequests == null) {
                stagedRequests = new LinkedList<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 {
            //没有相同的请求,那么把请求放进mWaitingRequests(一个hashmap集合)中,同时也放进mCacheQueue缓存队列中
            //这代表这个请求已经开始在缓存线程中运行了 
            mWaitingRequests.put(cacheKey, null);
            mCacheQueue.add(request);
        }
        return request;
    }
}


4,磁盘缓存默认是5m

RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
   // No maximum size specified
   queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
   // Disk cache size specified
   queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
public class DiskBasedCache implements Cache {

    /** Map of the Key, CacheHeader pairs */
    private final Map<String, CacheHeader> mEntries =
            new LinkedHashMap<String, CacheHeader>(16, .75f, true);

    /** Total amount of space currently used by the cache in bytes. */
    private long mTotalSize = 0;

    /** The root directory to use for the cache. */
    private final File mRootDirectory;

    /** The maximum size of the cache in bytes. */
    private final int mMaxCacheSizeInBytes;
/** Default maximum disk usage in bytes. */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;

/** High water mark percentage for the cache */
private static final float HYSTERESIS_FACTOR = 0.9f;

/** Magic number for current version of cache file format. */
private static final int CACHE_MAGIC = 0x20150306;

/**
 * Constructs an instance of the DiskBasedCache at the specified directory.
 * @param rootDirectory The root directory of the cache.
 * @param maxCacheSizeInBytes The maximum size of the cache in bytes.
 */
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
    mRootDirectory = rootDirectory;
    mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}

/**
 * Constructs an instance of the DiskBasedCache at the specified directory using
 * the default maximum cache size of 5MB.
 * @param rootDirectory The root directory of the cache.
 */
public DiskBasedCache(File rootDirectory) {
    this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}

 

5,默认开启了5个线程,4个网络调度线程NetworkDispatch,1个缓存调度线程CacheDispatch.

    

public void start() {
    stop();  // Make sure any currently running dispatchers are stopped.
    // Create the cache dispatcher and start it.
    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
    mCacheDispatcher.start();

    // 创建网络线程,默认长度就是核心线程的数量,4个
    for (int i = 0; i < mDispatchers.length; i++) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}


6,Volley分为3类线程:主线程,缓存调度线程(默认1个),网络调度线程(默认4个);
        6.1根据优先级将请求添加到缓存队列
        6.2缓存调度线程,从缓存队列中取出1个请求
        6.3如果找到该请求的缓存响应,就直接读取缓存的响应并解析,最后将解析的响应传递到主线程;
        6.4若请求丢失,则通过网络调度线程从网络队列中轮训取出请求,;
        6.5取出后,发送http请求,解析响应数据,存入缓存,最后将解析的响应传递到主线程

 

 

借鉴:刘望舒的进阶之光

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值