android的HTTP框架之Volley
Volley是android官方开发的一个HTTP框架,简化了利用java中原生的HTTP操作API-HttpURLConnection和HttpClient的操作。
一、首先是Volley的简单使用示例:
package com.dqxst.first; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.Toast; import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.Volley;
import com.dqxst.first.adapter.Images; import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; public class HttpActivity extends AppCompatActivity {
private final static int LOAD_FAIL = 0;
private final static int LOAD_SUCCESS = 1; private final Handler handler = new Handler() {
// private Context that=context;
@Override
public void handleMessage(Message msg) {
Bitmap photo = (Bitmap) msg.obj;
switch (msg.what) {
case LOAD_FAIL:
// Toast.makeText(that,"加载失败",Toast.LENGTH_SHORT).show();
break;
case LOAD_SUCCESS:
iv.setImageBitmap(photo);
}
}
}; private static ImageView iv;
private NetworkImageView niv; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http);
init();
// httpURLConnection();
// volley();
volley2();
volley3();
} private void volley3() {
//这是第三种加载图片的方式,其实内部还是第二种凡是进行实现,但是通过一种自定义控件的形式表现
RequestQueue queue=Volley.newRequestQueue(this);
ImageLoader loader=new ImageLoader(queue,new BitmapCache()); niv.setDefaultImageResId(R.drawable.loading);
niv.setErrorImageResId(R.drawable.load_error);
niv.setImageUrl(Images.imageThumbUrls[3],loader);
} private void volley2() {
//这是Volley中一种加载图片的方式,和最基本的Request方式不同,
//优点是在loader中可以传入一个用于缓存的参数,可以利用LruCache来进行缓存管理
RequestQueue queue = Volley.newRequestQueue(HttpActivity.this);
ImageLoader loader = new ImageLoader(queue, new BitmapCache());
ImageLoader.ImageListener listener = ImageLoader.getImageListener(iv,R.drawable.loading,R.drawable.load_error);
loader.get(Images.imageThumbUrls[2],listener);
} public class BitmapCache implements ImageLoader.ImageCache{
private int maxMemory=10*1024*1024;
private LruCache<String,Bitmap> cache=new LruCache<String,Bitmap>(maxMemory){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
@Override
public Bitmap getBitmap(String s) {
return cache.get(s);
} @Override
public void putBitmap(String s, Bitmap bitmap) {
cache.put(s,bitmap);
}
} private void volley() {
//Volley基本使用分3步
//1、创建RequestQueue请求队列,
RequestQueue queue = Volley.newRequestQueue(HttpActivity.this);
//2、创建一个请求对象,这里是加载图片的请求对象
ImageRequest request = new ImageRequest(Images.imageThumbUrls[1], new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
iv.setImageBitmap(bitmap);
}
}, 500, 500, null, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(), "加载出错", Toast.LENGTH_SHORT).show();
}
});
//3、最后,需要将请求对象添加到请求队列中进行工作。
queue.add(request);
} private void httpURLConnection() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Bitmap bitmap;
URL url = new URL(Images.imageThumbUrls[0]);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000);
con.setReadTimeout(10 * 1000);
bitmap = BitmapFactory.decodeStream(con.getInputStream());
Message msg = new Message();
if (bitmap != null) {
msg.what = LOAD_SUCCESS;
msg.obj = bitmap;
} else {
msg.what = LOAD_FAIL;
}
handler.sendMessage(msg);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} private void init() {
iv = (ImageView) findViewById(R.id.http_imageView);
niv= (NetworkImageView) findViewById(R.id.volley_NetworkImageView);
}
}
HttpActivity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.dqxst.first.HttpActivity"> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/http_imageView"
android:src="@drawable/loading"
android:contentDescription="test"/> <com.android.volley.toolbox.NetworkImageView
android:id="@+id/volley_NetworkImageView"
android:layout_width="200dp"
android:layout_height="200dp" />
</LinearLayout>
activity_http.xml
通过以上的基本使用代码,可以看到一些Volley的基本情况
1、由于android的UI线程的限制,所以需要在新建线程中进行网络操作,所以网络操作需要涉及线程之间的通信,而一般使用的HTTP框架都需要对其进行自动处理进行简化,Volley框架也是如此,关于具体实现见下文的源码分析部分
2、可以看到,Volley中最基本的使用就是第一种方式,就是通过继承Request对象来实现的请求方式。Volley本身就有StringRequest等一系列具体的实现。详细的说明见二。
二、继承Request实现的HTTP请求。
首先先看Volley中的StringRequest的实现,
package com.android.volley.toolbox; import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import java.io.UnsupportedEncodingException; public class StringRequest extends Request<String> {
private final Listener<String> mListener; //构造函数,主要作用有2,
//1、调用父类来进行初始化
//2、初始化监听类属性,用于监听Response服务器返回的结果
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
} public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(0, url, listener, errorListener);
} //该方法是响应的处理,调用监听器中的相应方法
protected void deliverResponse(String response) {
this.mListener.onResponse(response);
} //处理响应数据的方法,
//参数NetworkResponse是对响应结果的一种封装,包括了响应头和响应体data
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException var4) {
parsed = new String(response.data);
} return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
StringRequest源码
通过学习上面的实现过程,可以对Volley进行扩展实现,满足个性化的使用。例如,
1、对响应结果为XML的数据进行解析(就是将parseNetworkResponse中的数据通过xml的方式进行解析即可),
2、使用Gson/fastjson对JSON数据进行解析,因为Volley中使用的是Android中自带的json解析方式,并且分为JsonArrayRequest和JsonObjectRequest两个具体实现。
三、源码解析:
1、在使用Volley时,第一步是创建一个RequestQueue对象,通常是由Volley对象的方法创建而不是直接new一个,源码见下
RequestQueue queue = Volley.newRequestQueue(HttpActivity.this); public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
} public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0"; try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var6) {
;
} //1、创建http连接操作对象,其内部使用的是HttpURLConnection(sdk>=9)和HttpClient(sdk<9)
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
} //2、创建一个Network对象,这里使用的是Volley中的实现BasicNetwork,
//!!!该对象主要是调用上面的http连接进行连接,并对响应结果进行处理,
BasicNetwork network1 = new BasicNetwork((HttpStack)stack); //3、创建RequestQueue对象,并调用start(),这一部分解释见2
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
queue1.start();
return queue1;
}
通过Volley对象创建RequestQueue
2、RequestQueue对象是整个Volley中最重要的一个对象,它的一部分功能类似于一个线程池(可以生成和管理线程)
public void start() {
this.stop(); //1、 创建一个mCacheDispatcher并调用其start(),其实就是创建一个缓存线程
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start(); //2、这里默认循环4次,创建4个NetworkDispatcher对象并调用start(),其实是创建四个工作线程
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
} }
RequestQueue.start()
3、使用Volley的最后一步就是使用RequestQueue的add()将实现的Request加入到队列中,如queue.add(request);
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
} request.setSequence(this.getSequenceNumber());
request.addMarker("add-to-queue");
//这里判断的是请求是否可以缓存,使用的是mShouldCache属性,默认值为true,即可以缓存
if(!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
} else {
Map var8 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
if(this.mWaitingRequests.containsKey(cacheKey)) {
Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if(stagedRequests == null) {
stagedRequests = new LinkedList();
} ((Queue)stagedRequests).add(request);
this.mWaitingRequests.put(cacheKey, stagedRequests);
if(VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
}
} else {
this.mWaitingRequests.put(cacheKey, (Object)null);
//在这里将Request对象加入到缓存队列中进行操作
this.mCacheQueue.add(request);
} return request;
}
}
}
add()源码
4、通过上面的源码可以看到,最终的操作是落在
public class CacheDispatcher extends Thread { @Override
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();
//这里表明该线程一直在运行
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 检测请求是否被取消,是则退出
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 检测是否有缓存的结果,没有则调用NetworkQueue重新进行请求
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 (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.
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
}
CacheDispatcher的run()
public void run() {
Process.setThreadPriority(10); //外层循环,保证工作线程持续工作
while(true) {
Request request;
//内层循环,循环访问工作队列,从其中取出需要处理的任务,
//注意这里使用了take(),是一种阻塞的实现,详见线程深入学习二
while(true) {
try {
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
} try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
request.finish("network-discard-cancelled");
} else {
this.addTrafficStatsTag(request);
//这里说明真正执行http的是mNetwork对象,
//Volley中默认就是HttpURLConnection(API9之后)或HttpClient
//这在1中源码部分可见,
//最终将HTTP相应封装成NetworkResponse
NetworkResponse e = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//这里是检测的notModified属性其实是检测返回的状态码是否为304
//第二个是判断响应是否已经提交过
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response response = request.parseNetworkResponse(e);
request.addMarker("network-parse-complete");
//!!!在这里将获取的数据进行缓存
if(request.shouldCache() && response.cacheEntry != null) {
this.mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
} request.markDelivered();
this.mDelivery.postResponse(request, response);
}
}
} catch (VolleyError var5) {
this.parseAndDeliverNetworkError(request, var5);
} catch (Exception var6) {
VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
this.mDelivery.postError(request, new VolleyError(var6));
}
}
}
NetworkDispatcher的run()
看完了源码的简要分析,就可以搞明白Volley官方对其进行解释的一幅图了,如下:
四、其他问题:
1、Volley发送POST请求
通过上面的源码分析,可以知道最终的HTTP操作会在
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if(this.mUrlRewriter != null) {
String parsedUrl = this.mUrlRewriter.rewriteUrl(url);
if(parsedUrl == null) {
throw new IOException("URL blocked by rewriter: " + url);
} url = parsedUrl;
} URL parsedUrl1 = new URL(url);
//创建HttpURLConnection对象用于连接,下面的方法就是对连接进行基本设置
HttpURLConnection connection = this.openConnection(parsedUrl1, request);
Iterator protocolVersion = map.keySet().iterator(); while(protocolVersion.hasNext()) {
String responseCode = (String)protocolVersion.next();
connection.addRequestProperty(responseCode, (String)map.get(responseCode));
} //这个方法是根据请求方式对请求进行一些设置,主要就是对一些请求如POST的参数进行传递
setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
int responseCode1 = connection.getResponseCode();
if(responseCode1 == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
Iterator i$ = connection.getHeaderFields().entrySet().iterator(); while(i$.hasNext()) {
Entry header = (Entry)i$.next();
if(header.getKey() != null) {
BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
} return response;
}
} //对请求进行一些基本设置
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = this.createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
if("https".equals(url.getProtocol()) && this.mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(this.mSslSocketFactory);
} return connection;
} //根据请求方式进行设置,主要是对POST等请求传递参数
static void setConnectionParametersForRequest(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError {
switch(request.getMethod()) {
case -1:
//!!!这里调用请求对象来获取POST传递的参数
byte[] postBody = request.getPostBody();
if(postBody != null) {
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty("Content-Type", request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case 0:
connection.setRequestMethod("GET");
break;
case 1:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case 2:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case 3:
connection.setRequestMethod("DELETE");
break;
case 4:
connection.setRequestMethod("HEAD");
break;
case 5:
connection.setRequestMethod("OPTIONS");
break;
case 6:
connection.setRequestMethod("TRACE");
break;
case 7:
addBodyIfExists(connection, request);
connection.setRequestMethod("PATCH");
break;
default:
throw new IllegalStateException("Unknown method type.");
} } private static void addBodyIfExists(HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError {
byte[] body = request.getBody();
if(body != null) {
connection.setDoOutput(true);
connection.addRequestProperty("Content-Type", request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
} }
HurlStack的performRequest()
从上面可以看到最终调用Request去获取POST传递的参数,而最终调用的方法就是getParams()。
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
getParams()
结论:要实现POST请求,需要重写Request中的getParams()方法传递参数,并将method设置为1或-1(为-1时是判断是否有请求参数,即getParams()是否为null)。
注意:上面是将参数以普通的形式即键值对的形式进行发送,但有时需要以json串的形式进行发送,此时就需要使用JsonObjectRequest/JsonArrayRequest,并需要重写getHeaders()添加json处理的头信息
@Override
public Map<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
headers.put("Content-Type", "application/json; charset=UTF-8"); return headers;
}
json处理头信息
参考:http://blog.csdn.net/guolin_blog/article/details/17482095
http://blog.csdn.net/gaolu/article/details/38439375
2、加载图片:在一中的基本使用部分代码里,有两种专门针对图片的操作方式,一个是使用ImageLoader,一个是使用自定义图片控件NetworkImageView。从本质上来说两者使用的都是ImageLoader进行实现的。两者的区别在于:
①如果要是针对普通的图片展示形式(因为NetworkImageView仅仅是增加了通过URL来添加图片的功能,基本显示还是ImageView)可以使用该自定义控件,更简便一些;
②如果有特殊的展示需求,例如需要圆形图片而使用自定义控件(circularimageview)时可以使用ImageLoader方式。
其实通过源码可以发现,ImageLoader就是对Request方式进行封装,可以将图片直接显示在指定的ImageView控件上而已,方便了用户的操作。但是最主要的一个区别是:使用这种方式没有使用默认的磁盘缓存DiskBaseedCache方案,而是需要用户指定缓存的方式。
3、Volley的缓存:在上面对缓存介绍的比较少,其实缓存是Volley中比较重要的部分。有了缓存可以降低HTTP请求的次数,提升用户的体验。从上图中可以看到Volley首先是从缓存中进行查询,如果缓存没有才对服务器进行请求。具体见如下分析。
①首先是在创建RequestQueue对象时需要传入一个实现了Cache接口的对象,通过Volley类创建的时候默认使用的就是DiskBasedCache,从名字可以看到这是一个机遇硬盘的缓存方案,缓存路径就是应用的cache目录(可以从上面的3.1部分源码看到)。
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
this.mSequenceGenerator = new AtomicInteger();
this.mWaitingRequests = new HashMap();
this.mCurrentRequests = new HashSet();
this.mCacheQueue = new PriorityBlockingQueue();
this.mNetworkQueue = new PriorityBlockingQueue();
this.mCache = cache;
this.mNetwork = network;
this.mDispatchers = new NetworkDispatcher[threadPoolSize];
this.mDelivery = delivery;
} public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
} public RequestQueue(Cache cache, Network network) {
this(cache, network, 4);
}
RequestQueue的构造函数
②对于DiskBaseedCache对象,会在Volley创建RequestQueue时传入,并在其start()用于创建CacheDispatcher对象,作用域缓存。最后会在其run()中被初始化。
//Volley.newRequestQueue()
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1); //RequestQueue。start()
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start(); //CacheDispatcher.run()
this.mCache.initialize();
DiskBasedCache的创建和初始化过程
③缓存操作:默认的存储路径是data/data/<package>/Volley下。有了DiskBaseedCache缓存对象之后就可以实现保存(在NetworkDispatcher中)和取出缓存(在CacheDispatcher中)的操作,通过调用其put()/get()实现,还可以通过调用其remove()方法删除缓存。
当网络断开的情况下,Volley同样首先从缓存中获取数据,会先判断其是否过期,根据缓存中的ttl,就是缓存头的MaxAge。
4、多线程间通信的实现:其内部使用了handler机制。解析如下:
①在通过网络线程或者缓存线程获取到响应之后,都会调用一个方法来提交响应
//CacheDispatcher的run()中部分
this.mDelivery.postResponse(e, response); this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
} }
}); //NetWorkDispatcher的run()中部分
this.mDelivery.postResponse(request, response);
提交响应结果
②默认使用的是ExecutorDelivery来进行提交响应结果
//RequestQueue构造函数,传入ExecutorDelivery对象执行提交响应
//并且将绑定主UI线程的Handler对象传入
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
} //ExecutorDelivery部分
public ExecutorDelivery(final Handler handler) {
this.mResponsePoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
}
};
}
//执行提交的部分
public void postResponse(Request<?> request, Response<?> response) {
this.postResponse(request, response, (Runnable)null);
}
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
ExecutorDelivery
③经过上述处理,最终实际提交的是其内部的ResponseDeliveryRunnable。由Handler机制可知,最后处理消息就是该Runnable的run().在向下就可以看到最后使用了我们提供的监听器中的方法进行处理,实现了线程间通信。
public void run() {
if(this.mRequest.isCanceled()) {
this.mRequest.finish("canceled-at-delivery");
} else {
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);
} else {
this.mRequest.deliverError(this.mResponse.error);
} if(this.mResponse.intermediate) {
this.mRequest.addMarker("intermediate-response");
} else {
this.mRequest.finish("done");
} if(this.mRunnable != null) {
this.mRunnable.run();
} }
}
处理消息的run()
五、源码分析续之HTTP处理
上面的源码分析中没有分析Volley中是如何具体处理HTTP协议的,在这里进行分析。从上面知道,Volley的网络请求最终是由BasicNetwork这个类进行调度处理的(真正的处理时HttpURLConnection类),当然也可以自定义一个处理类,只要实现Network接口,完成其中的网络操作的方法即可。
public interface Network {
NetworkResponse performRequest(Request<?> var1) throws VolleyError;
}
1、下面是BasicNetwork中的performRequest(),就是通过这个方法对HTTP请求过程进行调度的。但是实际的请求并不在这里执行,见下面分析:
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime(); while(true) {
HttpResponse httpResponse = null;
Object responseContents = null;
HashMap responseHeaders = new HashMap(); try {
HashMap e = new HashMap();
this.addCacheHeaders(e, request.getCacheEntry());
httpResponse = this.mHttpStack.performRequest(request, e);
StatusLine statusCode2 = httpResponse.getStatusLine();
int networkResponse1 = statusCode2.getStatusCode();
Map responseHeaders1 = convertHeaders(httpResponse.getAllHeaders());
if(networkResponse1 != 304) {
byte[] responseContents1;
if(httpResponse.getEntity() != null) {
responseContents1 = this.entityToBytes(httpResponse.getEntity());
} else {
responseContents1 = new byte[0];
} long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime, request, responseContents1, statusCode2);
if(networkResponse1 >= 200 && networkResponse1 <= 299) {
return new NetworkResponse(networkResponse1, responseContents1, responseHeaders1, false);
} throw new IOException();
} return new NetworkResponse(304, request.getCacheEntry() == null?null:request.getCacheEntry().data, responseHeaders1, true);
} catch (SocketTimeoutException var12) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException var13) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException var14) {
throw new RuntimeException("Bad URL " + request.getUrl(), var14);
} catch (IOException var15) {
boolean statusCode = false;
NetworkResponse networkResponse = null;
if(httpResponse == null) {
throw new NoConnectionError(var15);
} int statusCode1 = httpResponse.getStatusLine().getStatusCode();
VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode1), request.getUrl()});
if(responseContents == null) {
throw new NetworkError(networkResponse);
} networkResponse = new NetworkResponse(statusCode1, (byte[])responseContents, responseHeaders, false);
if(statusCode1 != 401 && statusCode1 != 403) {
throw new ServerError(networkResponse);
} attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
}
}
}
①请求时间记录:在2,24,25行可以看到有记录整个请求时间的数据并且进行打印
②执行HTTP请求,使用的是mHttpStack进行操作,现在主要使用的就是HurlStack进行的,其内部使用HttpURLConnection(在下面进行说明)。
③对部分状态码的结果进行处理(16-31行):注意,这里处理的状态码只有200-299和304,其余的状态码都抛出IOException,在异常处理中尝试处理。
a、304状态:说明请求的文档的缓存仍然有效,则直接将缓存数据直接封装在最终的响应中以供使用。
b、2xx状态:对于非304的状态,进行的处理是将其中的content信息从流中读取出来存放到responseContents1属性中,还有之前就已经读取的头信息集合放在responseHeaders1属性中。如果状态在200-299之间,则将这些数据进行封装最终返回,其他状态则抛出异常。
④异常处理:从③可知,这里的异常可能不是真正的异常,处理过程如下,
a、超时(连接超时和读取超时):针对两种不同的连接方式(HttpURLConnection/HttpClient)的不同异常进行处理,就是执行重试策略。默认的重试策略就是超时时间增加一倍然后重试1次。超时时间增加是由乘积因子决定的。
b、URL格式错误:直接报错
c、IOException:该异常可能有两种情况引起:
1、确实是IOException即读取过程的异常:这部分就是对响应结果进行检测,如果响应的结果为null则报错。(在42-51行)
2、由于上面没有处理的状态码报错:如果是401或403状态,直接报错,其他状态进行重试。
2、执行HTTP请求的就是HurlStack中的方法,下面对其过程进行说明:
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if(this.mUrlRewriter != null) {
String parsedUrl = this.mUrlRewriter.rewriteUrl(url);
if(parsedUrl == null) {
throw new IOException("URL blocked by rewriter: " + url);
} url = parsedUrl;
} URL parsedUrl1 = new URL(url);
HttpURLConnection connection = this.openConnection(parsedUrl1, request);
Iterator protocolVersion = map.keySet().iterator(); while(protocolVersion.hasNext()) {
String responseCode = (String)protocolVersion.next();
connection.addRequestProperty(responseCode, (String)map.get(responseCode));
} setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
int responseCode1 = connection.getResponseCode();
if(responseCode1 == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
Iterator i$ = connection.getHeaderFields().entrySet().iterator(); while(i$.hasNext()) {
Entry header = (Entry)i$.next();
if(header.getKey() != null) {
BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
} return response;
}
}
android的HTTP框架之Volley的更多相关文章
- Android热门网络框架Volley详解[申明:来源于网络]
Android热门网络框架Volley详解[申明:来源于网络] 地址:http://www.cnblogs.com/caobotao/p/5071658.html
- Android框架之Volley与Glide
PS:在看到这个题目的同时,你们估计会想,Volley与Glide怎么拿来一块说呢,他们虽然不是一个框架,但有着相同功能,那就是图片处理方面.首先我们先来看一下什么volley,又什么是glide. ...
- 60.Android通用流行框架大全
转载:https://segmentfault.com/a/1190000005073746 Android通用流行框架大全 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的 ...
- 15类Android通用流行框架
15类Android通用流行框架 Android流行框架 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 图片加载 Android Universal Image Loader 一个 ...
- Android图片载入框架最全解析(一),Glide的基本使用方法
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53759439 本文同步发表于我的微信公众号.扫一扫文章底部的二维码或在微信搜索 郭 ...
- Java/Android 网络请求框架/库
Android 图片缓存框架 最上面的最优先 com.facebook.fresco:fresco:0.12.0 7.26.2016最新 Universal-Image ...
- 我的Android进阶之旅------>Android通用流行框架大全
Android通用流行框架大全 缓存 图片加载 图片处理 网络请求 网络解析 数据库 依赖注入 图表 后台处理 事件总线 响应式编程 Log框架 测试框架 调试框架 性能优化 本文转载于lavor的博 ...
- (转载)15 个 Android 通用流行框架大全
15 个 Android 通用流行框架大全 时间:2017-03-20 11:36来源:未知 作者:admin 点击: 2089 次 15 个 Android 通用流行框架大全 1. 缓存 Dis ...
- 各种Android UI开源框架 开源库
各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...
随机推荐
- 如何在ssh远程linux服务器时不需要输入密码
目的: 期望A服务器在对B服务器执行ssh或者scp等命令的时候不需要输入密码 实现方法: 1.通过安装sshpass服务 2.通过密钥验证的方式 操作过程: 一.通过sshpass的方式达到密码非交 ...
- mac pro配置php开发环境
mac pro自带php和apache,所以我们只要配置下就好了 // 启动Apache服务 sudo apachectl start // 重启Apache服务 sudo apachectl res ...
- UT源码+105032014018
设计佣金问题的程序 commission方法是用来计算销售佣金的需求,手机配件的销售商,手机配件有耳机(headphone).手机壳(Mobile phone shell).手机贴膜(Cellphon ...
- Python格式化字符串、占位符、合并数组
合并数组 参考链接:https://www.cnblogs.com/chaihy/p/7243143.html >>> a=[2] >>> b=[3] >&g ...
- numpy学习笔记 - numpy数组的常见用法
# -*- coding: utf-8 -*- """ 主要记录代码,相关说明采用注释形势,供日常总结.查阅使用,不定时更新. Created on Mon Aug 20 ...
- HTTP——学习笔记(2)
HTTP协议通信双方一定是客户端和服务器端,而且一定是由客户端发出请求,由服务器接受请求 客户端发送的报文的构成: 服务器端收到请求后响应的报文构成: 客户端向服务器端发送请求有多种方法: get:获 ...
- Hibernate持久化步骤
1. 读取并解析配置文件 Configuration config= new Configuration().configure(); 相当于使用DataSource获取连接前读取DataSour ...
- OpenJudge 1031 Lausanne Capitale Olympique
Lausanne Capitale Olympique Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on ...
- ajax前台传到后台乱码,显示问号的问题
response.setContentType("text/html;charset=gbk"); response.setHeader("Cache-Control&q ...
- IPA打包图片错误问题
CopyPNGFile /Users/gongihou/Library/Developer/Xcode/DerivedData/KTVgo-frborfduejxrajgpkfdaipygijow/B ...