android面试(11)-Volley

忙活了半天,终于把异步消息处理机制差不多都介绍完了,赶紧趁热打铁,从今天开始,就开始复习一些市面上比较有名的开源框架,每个开源框架大致都分为两步来说道说道,先谈一下怎么使用,会有代码实例,其次在讲一下关于源码的分析,好了,话不多说,现在就开始吧。

今天先讲Volley,volley是google推出的异步加载的一个网络框架,功能很强大,它适合那些数据量小的但是需要频繁去获取的情况。

1.简单使用:

(1)创建高并发的请求队列

(2)创建新的请求

(3)将请求添加到请求队列中

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);


    //Volley第一步:获取RequestQueue对象
    queue = Volley.newRequestQueue(this);


}

private void VolleyStringRequest(){

    //volley第二步,创建新的Request
    StringRequest request=new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            Log.e("返回成功的数据",response);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.e("返回失败的原因",error.getMessage(),error);
        }
    });

    //volley第三步,将request添加到RequestQueue中
    queue.add(request);
}

2.源码分析:

一步一步来看,先从第一步来看,看看他的newRequestQueue方法:

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

    if (stack == null) {
        if (Build.VERSION.SDK_INT >= 9) {
            stack = new HurlStack();
        } else {
            // Prior to Gingerbread, HttpUrlConnection was unreliable.
            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
        }
    }

    Network network = new BasicNetwork(stack);
    
    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);
    }

    queue.start();

    return queue;
}

从这里我们可以看到,当stack==null时,手机版本如果大于9的话,他会创建一个HurlStack对象,这个HurlStack对象是什么呢,我截取它的一段代码就可以明白了

URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
    connection.addRequestProperty(headerName, map.get(headerName));
}

这段代码在他的performRequest方法中,看起来非常熟悉,没错,这个HurlStack内部其实就是使用android原生的HTTPURLConnection来进行网络请求的。

继续往下看,可以看到,它会根据创建好的stack对象来创建NetWork对象,然后根据NetWork对象来创建RequestQueue对象,并调用其start方法,最后再将这个RequestQueue对象返回;

我们再来看看这个start方法到底做了些什么,

/**
 * Starts the dispatchers in this queue.
 */
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();

    // Create network dispatchers (and corresponding threads) up to the pool size.
    for (int i = 0; i < mDispatchers.length; i++) {
        NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                mCache, mDelivery);
        mDispatchers[i] = networkDispatcher;
        networkDispatcher.start();
    }
}

可以看到,start方法里面new了两种对象,一个是CacheDispatcher,从名字我们就知道他是一个缓存分发器,而实质上,它其实就是一个线程,内部其实是继承Thread的,NetWorkDispatcher,顾名思义,就是网络请求的分发器,看到这两个对象,我们就可以大致的想象出Volley的请求过程了;

当Volley进行请求时,他会开启两中请求线程,缓存分发器和网络分发器,当请求发起时,会先在缓存中找,如果缓存有,那么直接调用缓存中额数据,如果缓存中没有,则会请求网络去获取数据,获取完数据之后,还会向缓存中写一份。

还没完呢,这才是第一步,我们接下来看第三步的queue.add()方法,这里面代码比较繁琐,我截取其中比较重要的一段来看看

// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
    mNetworkQueue.add(request);
    return request;
}

这段代码表示,add之前,会判断当前的请求是否可以进行缓存,如果不可以,那么直接添加到网络请求队列中,否则,就添加到缓存请求队列中,默认的,所有请求都是可以进行缓存的;

既然我们一直在提什么缓存队列,网络请求队列,那我们就来看看这个CacheDispatcher到底是个什么

之前说过CacheDispatcher是一个线程,那么我们就来看看它的run方法:

public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    // Make a blocking call to initialize the cache.
    mCache.initialize();

    Request<?> request;
    while (true) {
        // release previous request object to avoid leaking request object when mQueue is drained.
        request = null;
        try {
            // Take a request from the queue.
            request = mCacheQueue.take();
        } catch (InterruptedException e) {
            // We may have been interrupted because it was time to quit.
            if (mQuit) {
                return;
            }
            continue;
        }
        try {
            request.addMarker("cache-queue-take");

            // If the request has been canceled, don't bother dispatching it.
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // Attempt to retrieve this item from cache.
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                // Cache miss; send off to the network dispatcher.
                mNetworkQueue.put(request);
                continue;
            }

            // If it is completely expired, just send it to the network.
            if (entry.isExpired()) {
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                mNetworkQueue.put(request);
                continue;
            }

            // We have a cache hit; parse its data for delivery back to the request.
            request.addMarker("cache-hit");
            Response<?> response = request.parseNetworkResponse(
                    new NetworkResponse(entry.data, entry.responseHeaders));
            request.addMarker("cache-hit-parsed");

            if (!entry.refreshNeeded()) {
                // Completely unexpired cache hit. Just deliver the response.
                mDelivery.postResponse(request, response);
            } else {
                // Soft-expired cache hit. We can deliver the cached response,
                // but we need to also send the request to the network for
                // refreshing.
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);

                // Mark the response as intermediate.
                response.intermediate = true;

                // Post the intermediate response back to the user and have
                // the delivery then forward the request along to the network.
                final Request<?> finalRequest = request;
                mDelivery.postResponse(request, response, new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mNetworkQueue.put(finalRequest);
                        } catch (InterruptedException e) {
                            // Not much we can do about this.
                        }
                    }
                });
            }
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
        }
    }
}
代码比较长,我们挑重点的来看,可以看到,他里面其实是开启的一个while的死循环,说明,这个缓存分发器是一直在运行的,在
 // Attempt to retrieve this item from cache.
            Cache.Entry entry = mCache.get(request.getCacheKey());
            if (entry == null) {
                request.addMarker("cache-miss");
                // Cache miss; send off to the network dispatcher.
                mNetworkQueue.put(request);
                continue;
            }

这段代码中,它会尝试从缓存中读取数据,如果这个数据是空的话,就把请求添加到网络请求队列中;如果不是空的话,再判断缓存是否过期,如果没过期,那么就尝试解析数据,然后把解析后的数据回调,过期的话和空的处理方法是一样的;

再来看看NetWorkDiapatcher:

它的run方法和cache的run方法差不多,内部也是开启了一个while死循环,不同的是,它在里面会调用performRequest(request)方法去请求数据,当得到响应response时,它会先写一份进入缓存,然后再根据不同的request进行不同的数据解析,StringRequest就解析成字符串类型的数据,JsonObjectRequest就解析出Json数据,解析完成后会调用postResponse方法将结果回调给主线程;


总结来说,就是上面这张图,好好理解把握对比源码,就可以明白了;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值