一、概念
什麼是Volley
開發android應用很多時候都要涉及網絡操作,Android SDK中提供了HttpClient 和 HttpUrlConnection兩種方式用來處理網絡操作,但當應用比較複雜的時候需要我們編寫大量的代碼處理很多東西:圖像緩存,請求的調度等等;
而Volley框架就是爲解決這些而生的,它與2013年Google I/O大會上被提出:使得Android應用網絡操作更方便更快捷;抽象了底層Http Client等實現的細節,讓開發者更專注與產生RESTful Request。另外,Volley在不同的線程上異步執行所有請求而避免了阻塞主線程。
二、特點
如下是Volley框架的內部實現原理圖,藍色是主線程,黃色是緩存線程,綠色是網絡線程
- 自動調度網絡請求
- 多個併發的網絡連接
- 通過使用標準的HTTP緩存機制保持磁盤和內存響應的一致
- 支持請求優先級
- 支持取消請求的強大API,可以取消單個請求或多個
- 易於定製
- 健壯性:便於正確的更新UI和獲取數據
- 包含調試和追蹤工具
三、使用
1. 獲得String數據
volleybt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//新建請求隊列
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
//新建請求
String url = "https://www.baidu.com/?tn=sitehao123_15";
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
volleytext.setText(s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
volleytext.setText("加載失敗"+volleyError);
}
});
requestQueue.add(request);
}
});
2. 獲得json數據
json_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1 創建一個請求隊列
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
// 2 創建一個請求
String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
volleytext.setText(jsonObject.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
volleytext.setText("請求失敗" + volleyError);
}
});
// 3 將創建的請求添加到請求隊列中
requestQueue.add(jsonObjectRequest);
}
});
3. post獲得String數據
post_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1 創建一個請求隊列
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
// 2 創建一個post請求
String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api";
StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
volleytext.setText(s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
volleytext.setText("請求失敗" + volleyError);
}
}) {
};
// 3 將post請求添加到隊列中
requestQueue.add(stringRequest);
}
});
4. 自定義獲得xml數據
xml_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1 創建一個請求隊列
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
XMLRequest xmlRequest = new XMLRequest(
"http://flash.weather.com.cn/wmaps/xml/china.xml",
new Response.Listener<XmlPullParser>() {
@Override
public void onResponse(XmlPullParser response) {
try {
int eventType = response.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String nodeName = response.getName();
if ("city".equals(nodeName)) {
String pName = response.getAttributeValue(0);
Log.d("TAG", "pName is " + pName);
}
break;
}
eventType = response.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
requestQueue.add(xmlRequest);
}
});
需要重新一個xmlRequest繼承Request
public class XMLRequest extends Request<XmlPullParser> {
private final Response.Listener<XmlPullParser> mListener;
public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener,
Response.ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
public XMLRequest(String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
try {
String xmlString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlString));
return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (XmlPullParserException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(XmlPullParser response) {
mListener.onResponse(response);
}
}
5. 自定義獲得Gson解析後的數據
gson_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
GsonRequest<Weather> gsonRequest = new GsonRequest<Weather>(
"http://www.weather.com.cn/data/sk/101010100.html", Weather.class,
new Response.Listener<Weather>() {
@Override
public void onResponse(Weather weather) {
WeatherInfo weatherInfo = weather.getWeatherinfo();
Log.d("TAG", "city is " + weatherInfo.getCity());
Log.d("TAG", "temp is " + weatherInfo.getTemp());
Log.d("TAG", "time is " + weatherInfo.getTime());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
requestQueue.add(gsonRequest);
}
});
同樣需要定義一個GsonRequest繼承Request
public class GsonRequest<T> extends Request<T> {
private final Response.Listener<T> mListener;
private Gson mGson;
private Class<T> mClass;
public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
super(method, url, errorListener);
mGson = new Gson();
mClass = clazz;
mListener = listener;
}
public GsonRequest(String url, Class<T> clazz, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
this(Method.GET, url, clazz, listener, errorListener);
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(mGson.fromJson(jsonString, mClass),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
//用於解析的類
class Weather {
private WeatherInfo weatherinfo;
public WeatherInfo getWeatherinfo() {
return weatherinfo;
}
public void setWeatherinfo(WeatherInfo weatherinfo) {
this.weatherinfo = weatherinfo;
}
}
還需要一個WeatherInfo類來解析Gson
public class WeatherInfo {
private String city;
private String temp;
private String time;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
四、原理
首先來梳理一下整體的流程,先建立也隊列,根據不同的系統版本建立不同的網絡初始化,接下來在這個隊列 中建立一個緩存進程和四個網絡進程,一共是五個進程,然後在就是往這個隊列中添加任務來。如果得到的請求有緩存資源就可以進行通過緩存線程執行任務了,如果沒有緩存就加入到網路線程任務中,處理完成後將response發送出去,我們在回調函數中就可以得到結果了。
(一)隊列創建
(1)網絡的選擇,是選擇HttpStack還是HttpClientStack
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 {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
(2)建立一個緩存線程和四個網絡線程
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
(二)增加請求
(1)加入隊列中
public <T> Request<T> add(Request<T> 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;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
(2)通過while循環的方式進行相應,如果緩存爲空就加入到網絡線程中
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 the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
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 it is completely expired, just send it to the network.
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;
}
}
}
}
(3)最後進行事件的分發
尊重作者,尊重原創,參考文章:
http://blog.csdn.net/guolin_blog/article/details/17656437
http://www.cnblogs.com/zyw-205520/p/4950357.html