Volley源码解析

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

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


Volley基本用法:

	@Override
	protected 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>() {
			@Override
			public void onResponse(String response) {
				String txt = new String(response.getBytes());
				((TextView) findViewById(R.id.tv)).setText(txt);
				System.out.println(txt);

			}
		}, new ErrorListener() {
			@Override
			public void onErrorResponse(VolleyError error) {
			}
		}));
		
		//一个请求队列可加入多个请求,默认会选择4个线程中的一个去执行网络请求
		mQueue.add(
				new ImageRequest("http://imgstatic.baidu.com/img/image/shouye/fanbingbing.jpg", new Listener<Bitmap>() {
					@Override
					public void onResponse(Bitmap response) {
						((ImageView) findViewById(R.id.iv1)).setImageBitmap(response);
					}
				}, 0, 0, Config.RGB_565, new ErrorListener() {
					@Override
					public 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() {
			
			@Override
			public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值