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);
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; }
- 【Volley】Volley源码解析
- volley源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- WINDOWS API——OPENCLIPBOARD——剪切板
- java.lang.IncompatibleClassChangeError
- 把$date = '08/26/2003' 转换成2003/08/26
- asp.net发布到IIS中出现错误:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”
- 程序员常用Photoshop工具教程 (格式转换,裁剪)
- Volley源码解析
- Android使用Webview显示网页
- WKWebView 加载Html
- dedecms自定义获取上级栏目名称和链接的标签 (返回上级栏目按钮)
- 1877: [SDOI2009]晨跑 费用流
- Linux中JDK1.6的安装和配置方法
- 备忘录模式
- Noi2001食物链
- cocos2d-x中android中设置横屏和竖屏