Volley源码解析

来源:互联网 发布:2017年淘宝好做吗 编辑:程序博客网 时间:2024/06/05 18:31

Volley支持网络图片、文本获取,内部会自动在内存,文件中缓存图片。

Volley还可以使用第三方的网络请求类库,只需在创建请求队列时实现相应接口即可。


Volley基本用法:

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);// 创建 RequestQueue RequestQueue mQueue = Volley.newRequestQueue(this);//构造请求,并加入到RequestQueue中。只需加入,即可自动执行网络请求mQueue.add(new StringRequest("http://www.baidu.com", new Listener<String>() {@Overridepublic void onResponse(String response) {String txt = new String(response.getBytes());((TextView) findViewById(R.id.tv)).setText(txt);System.out.println(txt);}}, new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {}}));//一个请求队列可加入多个请求,默认会选择4个线程中的一个去执行网络请求mQueue.add(new ImageRequest("http://imgstatic.baidu.com/img/image/shouye/fanbingbing.jpg", new Listener<Bitmap>() {@Overridepublic void onResponse(Bitmap response) {((ImageView) findViewById(R.id.iv1)).setImageBitmap(response);}}, 0, 0, Config.RGB_565, new ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {}}));//第2种图片加载方式(推荐使用)ImageView imageView = (ImageView) findViewById(R.id.iv2);ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate,android.R.drawable.ic_delete);ImageLoader mImageLoader = new ImageLoader(mQueue, new BitmapCache());mImageLoader.get("http://imgstatic.baidu.com/img/image/shouye/fanbingbing.jpg", listener); }

源码分析

使用Volley的第一步是创建请求队列,即:

RequestQueue mQueue = Volley.newRequestQueue(this);

Volley.newRequestQueue(getApplicationContext(), new HttpStack() {@Overridepublic HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)throws IOException, AuthFailureError {/* * 这里可以使用第三方的网络请求类库,网络请求所需的参数都已经封装在了request中, * additionalHeaders中是部分请求头信息。 *  * 执行完网络请求后只需构造一个org.apache.http.HttpResponse, * 并把返回的数据(相应头,状态码,body等)封装在里面并返回即可 */return null;}});

第二种方法中使用了自定义的网络请求类库,当该mQueue中add请求时,就会使用自定义的网络请求类库去执行网络请求。接下来看一下请求队列的创建过程 
 public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        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 = new RequestQueue(new DiskBasedCache(cacheDir), network);        queue.start();        return queue;    }
可以看到当stack不是null时就使用自定义的网络请求类库,否则就使用默认的,当Build.VERSION.SDK_INT >= 9时就使用Volley中的HurlStack,打开HurlStack可以看到用的是HttpURLConnection ,当<9时用的是Volley中的HttpClientStack,打开HttpClientStack可以看到用的是HttpUriRequest

HurlStack中重写的public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)throws IOException, AuthFailureError  

 

  @Override    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)            throws IOException, AuthFailureError {        String url = request.getUrl();        HashMap<String, String> map = new HashMap<String, String>();        map.putAll(request.getHeaders());        map.putAll(additionalHeaders);        if (mUrlRewriter != null) {            String rewritten = mUrlRewriter.rewriteUrl(url);            if (rewritten == null) {                throw new IOException("URL blocked by rewriter: " + url);            }            url = rewritten;        }        URL parsedUrl = new URL(url);        HttpURLConnection connection = openConnection(parsedUrl, request);        for (String headerName : map.keySet()) {            connection.addRequestProperty(headerName, map.get(headerName));        }        setConnectionParametersForRequest(connection, request);        // Initialize HttpResponse with data from the HttpURLConnection.        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);        int responseCode = connection.getResponseCode();        if (responseCode == -1) {            // -1 is returned by getResponseCode() if the response code could not be retrieved.            // Signal to the caller that something was wrong with the connection.            throw new IOException("Could not retrieve response code from HttpUrlConnection.");        }        StatusLine responseStatus = new BasicStatusLine(protocolVersion,                connection.getResponseCode(), connection.getResponseMessage());        BasicHttpResponse response = new BasicHttpResponse(responseStatus);        response.setEntity(entityFromConnection(connection));        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {            if (header.getKey() != null) {                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));                response.addHeader(h);            }        }        return response;    }


HttpClientStack中重写的public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)throws IOException, AuthFailureError  

 

 @Override    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)            throws IOException, AuthFailureError {        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);        addHeaders(httpRequest, additionalHeaders);        addHeaders(httpRequest, request.getHeaders());        onPrepareRequest(httpRequest);        HttpParams httpParams = httpRequest.getParams();        int timeoutMs = request.getTimeoutMs();        // TODO: Reevaluate this connection timeout based on more wide-scale        // data collection and possibly different for wifi vs. 3G.        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);        return mClient.execute(httpRequest);    }

  ps:所以如果要使用第三方的网络请求类库时也可以仿照上面的写法      


创建请求队列时有这么一句话

Network network = new BasicNetwork(stack);
BasicNetwork中最重要的一个方法就是重写的Network中的
  public NetworkResponse performRequest(Request<?> request) throws VolleyError

该方法内部只是调用了stack.performRequest(request, headers);  

从而获取到网络数据,然后封装成一个Volley专用的NetworkResponse并返回,NetworkResponse中只是记录了http响应的状态码,响应头,返回的byte数组以及是否缓存。


现在只剩下创建请求队列的最后三步了

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        queue.start();        return queue;


RequestQueue构造函数中最终执行了如下构造函数

 public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        mDelivery = delivery;    }

NetworkDispatcher是Thread的直接子类,即该请求队列创建了几个线程(默认是4个),(当然还有一个CacheDispatcher线程)

run方法中mQueue是一个jdk中的线程安全的阻塞队列PriorityBlockingQueue。

由此可知四个线程都是死循环,不断从mQueue中获取request,没有就阻塞,有就执行网络请求,获取网络数据

@Override    public void run() {        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        Request request;        while (true) {            try {                // Take a request from the queue.                request = mQueue.take();            } catch (InterruptedException e) {                // We may have been interrupted because it was time to quit.                if (mQuit) {                    return;                }                continue;            }            try {                省略部分代码。。。                // Perform the network request.                NetworkResponse networkResponse = mNetwork.performRequest(request);

RequestQueue中的add方法就是往线程安全的阻塞队列PriorityBlockingQueue中添加请求的,所以你可以同时调用请求队列的add方法添加各种请求

  public Request add(Request request) {        // Tag the request as belonging to this queue and add it to the set of current requests.        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);        }        // Process requests in the order they are added.        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // If the request is uncacheable, skip the cache queue and go straight to the network.        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }

执行完网络请求后需要对返回的数据进行处理(parseNetworkResponse方法是Request中需要重写的方法。networkResponse 中记录的不是字符串也不是图片,而是原始的byte数组, 如果你想获取图片,那你就需要使用ImageRequest,想获取字符串就使用StringRequest,所以到底该怎么处理数据由你的请求来决定,所以数据处理工作就放在了Request中而不是Response中)

Response<?> response = request.parseNetworkResponse(networkResponse);

处理完后就该回调监听了。NetworkDispatcher中run方法最后又这么一句

 mDelivery.postResponse(request, response);

通过查找代码我们发现mDelivery是在构造请求队列时创建的,同时使用UI线程的Looper创建了一个Handler,这样其他线程使用该handler发送的消息就会跑到UI线程中了。

 public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

在ExecutorDelivery中我们看到了最终调用了

 // Deliver a normal response or error, depending.            if (mResponse.isSuccess()) {                mRequest.deliverResponse(mResponse.result);            } else {                mRequest.deliverError(mResponse.error);            }
request中最终又调用了 mListener.onResponse(response);(以StringRequest为例)
 @Override    protected void deliverResponse(String response) {        mListener.onResponse(response);    }
Request中:
  public void deliverError(VolleyError error) {        if (mErrorListener != null) {            mErrorListener.onErrorResponse(error);        }    }




到这为止Volley的主要流程就分析完毕了。总结一下主要流程:

RequestQueue mQueue = Volley.newRequestQueue(this);   ->  mQueue.add(Request)到PriorityBlockingQueue中   ->多个NetworkDispatcher线程循环从PriorityBlockingQueue 读取Requset  -> 得到Requset然后执行网络请求 NetworkResponse networkResponse = mNetwork.performRequest(request);  ->得到networkResponse ,对networkResponse 中的原始数据进行处理(如 转换成图片或字符串)得到Response->通过ExecutorDelivery转移到主线程去回调监听器 -> 得到结果,请求结束 


图片加载源码解析

图片加载源码只不过是对封装了一下,底层实现类似。

先看一下如何使用Volley加载图片

ImageView imageView = (ImageView) findViewById(R.id.iv2);ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate,android.R.drawable.ic_delete);ImageLoader mImageLoader = new ImageLoader(mQueue, new BitmapCache());mImageLoader.get("http://imgstatic.baidu.com/img/image/shouye/fanbingbing.jpg", listener);


下面这句话没啥特殊的,就是new了一个Listener并返回,并在重写的方法中设置图片下载失败、成功时的图片
 ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate,android.R.drawable.ic_delete);

主要看这个函数

mImageLoader.get("http://imgstatic.baidu.com/img/image/shouye/fanbingbing.jpg", listener);
详见如下注释
   public ImageContainer get(String requestUrl, ImageListener imageListener,            int maxWidth, int maxHeight) {        // only fulfill requests that were initiated from the main thread.    // 是否在主线程中执行,不是就抛异常        throwIfNotOnMainThread();        /*         * 把图片路径以及大小拼接成一个字符串,作为map中的键,图片作为值,从         * 而可以根据键来快速从内存中获取图片(即 缓存到内存中,同时使用LRU算法对bitmap进行替换)         */        final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);        // Try to look up the request in the cache of remote images.        Bitmap cachedBitmap = mCache.getBitmap(cacheKey);        if (cachedBitmap != null) {            // Return the cached bitmap.        //把图片,url等封装一下作为参数传递给监听器            ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);            imageListener.onResponse(container, true);            return container;        }        // The bitmap did not exist in the cache, fetch it!        //如果内存中之前没有这张图片        ImageContainer imageContainer =                new ImageContainer(null, requestUrl, cacheKey, imageListener);        // Update the caller to let them know that they should use the default bitmap.        imageListener.onResponse(imageContainer, true);        // Check to see if a request is already in-flight.        BatchedImageRequest request = mInFlightRequests.get(cacheKey);        if (request != null) {            // If it is, add this request to the list of listeners.            request.addContainer(imageContainer);            return imageContainer;        }        // The request is not already in flight. Send the new request to the network and        // track it.        /*         * 创建一个ImageRequest,加入到请求队列中下载图片,下载成功后会把bitmap和cacheKey保         * 存到map中进行缓存。         * ImageRequest中的parseNetworkResponse会对图片进行压缩处理         */               Request<?> newRequest =            new ImageRequest(requestUrl, new Listener<Bitmap>() {                @Override                public void onResponse(Bitmap response) {                    onGetImageSuccess(cacheKey, response);                }            }, maxWidth, maxHeight,            Config.RGB_565, new ErrorListener() {                @Override                public void onErrorResponse(VolleyError error) {                    onGetImageError(cacheKey, error);                }            });        mRequestQueue.add(newRequest);        mInFlightRequests.put(cacheKey,                new BatchedImageRequest(newRequest, imageContainer));        return imageContainer;    }




0 0
原创粉丝点击