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开源 ... 
随机推荐
- 微信小程序的开启授权,当单机取消授权后 再次授权
			//单机去搜索 toSearch:function(){ this.getLocation(res => { console.log('成功') wx.navigateTo({ url: `.. ... 
- 紫书 例题8-17 UVa 1609 (构造法)(详细注释)
			这道题用构造法, 就是自己依据题目想出一种可以得到解的方法, 没有什么规律可言, 只能根据题目本身来思考. 这道题的构造法比较复杂, 不知道刘汝佳是怎么想出来的, 我想的话肯定想不到. 具体思路紫书上 ... 
- Linux进程管理之状态(二)
			二.进程的生命周期 进程是一个动态的实体,所以他是有生命的.从创建到消亡,是一个进程的整个生命周期.在这个周期中,进程可能会经历各种不同的状态.一般来说,所有进程都要经历以下的3个状态: 就绪态.指进 ... 
- 【codeforces 128C】Games with Rectangle
			[题目链接]:http://codeforces.com/problemset/problem/128/C [题意] 让你一层一层地在n*m的网格上画k个递进关系的长方形;(要求一个矩形是包含在另外一 ... 
- 【LeetCode OJ 34】Search for a Range
			题目链接:https://leetcode.com/problems/search-for-a-range/ 题目:Given a sorted array of integers, find the ... 
- 小贝_mysql select5种子句介绍
			mysql select5种子句介绍 简要 一.五种字句 二.具体解释五种字句 一.五种字句 where.group by.having.order by.limit 二.具体解释五种字句 2.1.理 ... 
- m_Orchestrate learning system---二十五、复制类的时候最容易出现的错误是什么
			m_Orchestrate learning system---二十五.复制类的时候最容易出现的错误是什么 一.总结 一句话总结:命名空间错误导致Analyze类虽然继承了Base类,但是没有执行里面 ... 
- django 笔记9 分页知识整理
			感谢老男孩 自定义分页 XSS:攻击 默认字符串返回 {{page_str|safe}} 前端 from django.utils.safestring import mark_safe page_s ... 
- Android RecyclerView实现横向滚动
			我相信很久以前,大家在谈横向图片轮播是时候,优先会选择具有HorizontalScrollView效果和ViewPager来做,不过自从Google大会之后,系统为我们提供了另一个控件Recycler ... 
- [NOI2010]海拔 平面图转对偶图 最小割
			题解: 首先,我们不难猜到高度只有 $0$ 或 $1$ 两种可能,而且高度为 0 的地区组成一个联通块,高度为 1 的地区组成一个联通块.只有这样,人们所耗费的体力才是最小的.得出这个结论,题目就成了 ... 
