前言
前一篇我們講解了HttpURLConnection
的使用郑叠,通過(guò)它我們可以發(fā)送和接收網(wǎng)絡(luò)數(shù)據(jù),在實(shí)際項(xiàng)目中使用率很高明棍。不過(guò)可能你們也發(fā)現(xiàn)了乡革,其用法還是有些復(fù)雜的,我們實(shí)際使用時(shí)還是需要對(duì)其再封裝摊腋。
本文將講述一個(gè)使用很簡(jiǎn)單的網(wǎng)絡(luò)通信框架Volley沸版。
文章篇幅略長(zhǎng),示例略多兴蒸,建議收藏 & 下載Demo配合閱讀
Demo地址:
Github:DemoVolley
相關(guān)文章
Android 網(wǎng)絡(luò)(一) HTTP協(xié)議
Android 網(wǎng)絡(luò)(二) HttpURLConnection用法解析
Android 網(wǎng)絡(luò)(四) Volley源碼解析
Android 網(wǎng)絡(luò)(五) OkHttp用法解析
Android 網(wǎng)絡(luò)(六) OkHttp源碼解析
Android 網(wǎng)絡(luò)(七) Retrofit用法解析
Android 網(wǎng)絡(luò)(八) Retrofit源碼解析
Volley簡(jiǎn)介
在2013年Google I/O大會(huì)上推出了一個(gè)新的網(wǎng)絡(luò)通信框架Volley
视粮。Volley
既可以訪問(wèn)網(wǎng)絡(luò)取得數(shù)據(jù),也可以加載圖片橙凳,并且在性能方面也進(jìn)行了大幅度的調(diào)整蕾殴。
特點(diǎn):
- 自動(dòng)調(diào)度網(wǎng)絡(luò)請(qǐng)求
- 支持多個(gè)并發(fā)的網(wǎng)絡(luò)連接
- 支持請(qǐng)求優(yōu)先級(jí)
- 可以取消單個(gè)或一系列請(qǐng)求
- 易于定制,如自定義請(qǐng)求方式
- 調(diào)試和跟蹤工具
使用場(chǎng)景:
- 非常適合去進(jìn)行數(shù)據(jù)量不大痕惋,但通信頻繁的網(wǎng)絡(luò)操作
- 不適合大數(shù)據(jù)量的網(wǎng)絡(luò)操作区宇,比如說(shuō)下載文件等
如何導(dǎo)入
- 可以通過(guò)下載
Volley
庫(kù)導(dǎo)入到libs\
下并add as a library
- 也可以通過(guò)
Maven
- 當(dāng)然還是更推薦通過(guò)
Gradle
添加依賴(lài)
implementation 'com.android.volley:volley:1.1.0'
使用前提
- 項(xiàng)目添加網(wǎng)絡(luò)訪問(wèn)權(quán)限
<uses-permission android:name="android.permission.INTERNET"/>
Volley網(wǎng)絡(luò)請(qǐng)求隊(duì)列
Volley
請(qǐng)求網(wǎng)絡(luò)是基于請(qǐng)求隊(duì)列的,我們只要簡(jiǎn)單的將請(qǐng)求放到請(qǐng)求隊(duì)列中就可以了值戳,它可以緩存所有的HTTP
請(qǐng)求议谷,然后按照一定的算法并發(fā)地發(fā)出這些請(qǐng)求。其內(nèi)部設(shè)計(jì)非常適合高并發(fā)堕虹,因此基本上一個(gè)應(yīng)用或一個(gè)Activity
持有一個(gè)請(qǐng)求隊(duì)列就足夠了卧晓。
Request
Volley
內(nèi)部為我們提供了一系列不同數(shù)據(jù)的Request API
芬首,同時(shí)由于其良好的擴(kuò)展性,我們也可以自定義我們需要的Request逼裆,對(duì)Request
的分類(lèi)如下圖所示:
類(lèi)型 | 相關(guān)API | 說(shuō)明 |
---|---|---|
基礎(chǔ)請(qǐng)求 |
StringRequest 郁稍,JsonRequest
|
Volley提供 |
圖片請(qǐng)求 |
ImageRequest ,ImageLoader 胜宇,NetworkImageView
|
Volley提供 |
自定義請(qǐng)求 |
XMLRequest 耀怜,GSONRequest
|
開(kāi)發(fā)者自己實(shí)現(xiàn) |
下面我們一個(gè)一個(gè)進(jìn)行學(xué)習(xí)
StringRequest的用法
StringRequest
是繼承自Request
類(lèi)的,屬于最基本的實(shí)現(xiàn)桐愉。
使用步驟:
- 創(chuàng)建
RequestQueue
對(duì)象 - 創(chuàng)建
StringRequest
對(duì)象 - 添加
StringRequest
對(duì)象到RequestQueue
對(duì)象中
是不是很簡(jiǎn)單财破!
下面展示一個(gè)實(shí)例,注釋已經(jīng)很詳細(xì)了从诲,就不再贅述:
//StringRequest
private void sendStringRequest(){
//當(dāng)然首先嘗試一下訪問(wèn)百度了
String url = "https://www.baidu.com";
/**
* 創(chuàng)建一個(gè)StringRequest對(duì)象
* 參數(shù)說(shuō)明:
* 1.請(qǐng)求方法
* 2.目標(biāo)服務(wù)器的URL地址
* 3.服務(wù)器響應(yīng)成功的回調(diào)
* 1.服務(wù)器響應(yīng)失敗的回調(diào)
**/
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
mTextView.setText("The StringRequest's response is "+ response.substring(0,500));
}
}, new Response.ErrorListener(){
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("The StringRequest's response is: That didn't work!" );
}
});
//將StringRequest放入請(qǐng)求隊(duì)列中即可
mQueue.add(stringRequest);
/*以下為POST方式左痢,需要傳遞數(shù)據(jù)時(shí)的處理
StringRequest stringRequestPost = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> map = new HashMap<String,String>();
map.put("param1","value1");
map.put("param2","value2");
return map;
}
}
mQueue.add(stringRequestPost);
*/
}
JsonRequest
JsonRequest
是很類(lèi)似StringRequest
的,同樣繼承自Request
類(lèi)系洛,這也是為什么我把二者都?xì)w為簡(jiǎn)單Request
的原因俊性。
不過(guò)JsonRequest
是一個(gè)抽象類(lèi),其有兩個(gè)實(shí)現(xiàn)類(lèi)分別為JsonObjectRequest
和JsonArrayRequest
描扯,從字面上我們都可以才得到:前者請(qǐng)求一段JSON
數(shù)據(jù)定页,后者請(qǐng)求一段JSON
數(shù)組
使用步驟:
- 創(chuàng)建
RequestQueue
對(duì)象 - 創(chuàng)建
JsonObjectRequest
或JsonArrayRequest
對(duì)象 - 2放入1中
實(shí)例:
//JSONRequest
private void sendJSONRequest(){
//網(wǎng)上找到的一個(gè)天氣的API,暫時(shí)可用荆烈,穩(wěn)定性待測(cè)= =
String url = "https://www.sojson.com/open/api/weather/json.shtml?city=北京";
//JsonObjectRequest和JsonArrayRequest是JsonRequest(抽象類(lèi))的子類(lèi)
//前者請(qǐng)求JSON數(shù)據(jù)拯勉,后者請(qǐng)求JSON數(shù)組
/**
* 參數(shù)說(shuō)明:
* 1:請(qǐng)求方法
* 2:服務(wù)器的URL地址
* 3:POST方式傳遞的JSON數(shù)據(jù)竟趾,如果為空則表示POST方式?jīng)]有要提交的參數(shù)
* 4:接收響應(yīng)成功時(shí)返回的JSON數(shù)據(jù)的監(jiān)聽(tīng)器
* 4:接收響應(yīng)失敗時(shí)返回的錯(cuò)誤信息的監(jiān)聽(tīng)器
*/
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
mTextView.setText("The JSONRequest's response is " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("The JSONRequest's response is: That didn't work!" );
}
});
//將請(qǐng)求放入請(qǐng)求隊(duì)列
mQueue.add(jsonObjectRequest);
}
是不是發(fā)現(xiàn)JsonObjectRequest
的用法和StringRequest
的用法基本一致憔购?這也是Volley
的強(qiáng)大之處,我們只要了解最基本的StringRequest
岔帽,就很容易舉一反三玫鸟。
提前劇透以下,后續(xù)的自定義Request本質(zhì)上就是變了形的StringRequest
ImageRequest
ImageRequest
是一個(gè)圖片請(qǐng)求對(duì)象犀勒,繼承自Request<Bitmap>
屎飘,得到的結(jié)果是一個(gè)Bitmap
。不過(guò)現(xiàn)在已經(jīng)是過(guò)時(shí)的方法了贾费。推薦后續(xù)下面兩種圖片請(qǐng)求方式钦购。
使用步驟:
- 創(chuàng)建
RequestQueue
對(duì)象。 - 創(chuàng)建
ImageRequest
對(duì)象褂萧。 - 將
ImageRequest
對(duì)象添加到RequestQueue
里面押桃。
實(shí)例
/ImageRequest
private void useImageRequest() {
//url地址,我的簡(jiǎn)書(shū)的頭像= =
String url = "https://upload.jianshu.io/users/upload_avatars/11024422/9960fc0a-0e86-4a1b-ba25-bca296e674c9.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300";
//創(chuàng)建ImageRequest對(duì)象
/**
* 參數(shù)說(shuō)明:
* 1. URL
* 2. 圖片請(qǐng)求成功的回調(diào)
* 34. 指定圖片最大的寬度和高度导犹,如果網(wǎng)絡(luò)圖片高度或?qū)挾却笥谶@里的最大值唱凯,就會(huì)對(duì)圖片進(jìn)行壓縮
* 如果指定為0則表示不管圖片多大都不會(huì)壓縮圖片
* 5. 指定顏色屬性
* 6. 圖片請(qǐng)求失敗的回調(diào)
*/
ImageRequest imageRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
mImageView.setImageBitmap(response);
}
}, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.mipmap.ic_launcher);
}
});
mQueue.add(imageRequest);
}
是不是又雙發(fā)現(xiàn)羡忘,這個(gè)使用步驟驚人的相似!這是因?yàn)樗蜕鲜鰞煞N基本Request同繼承自Request磕昼,因此處理方式自然不會(huì)有很大差異卷雕。
ImageLoader
ImageLoader
同樣用來(lái)加載網(wǎng)絡(luò)圖片,其內(nèi)部基于ImageRequest
票从,但是比ImageRequest
高效的多漫雕。它的構(gòu)造器可以傳入一個(gè)ImageCache
緩存形參,實(shí)現(xiàn)了圖片緩存的功能峰鄙,同時(shí)還可以過(guò)濾重復(fù)鏈接蝎亚,避免重復(fù)發(fā)送請(qǐng)求。
- 有一點(diǎn)顯著的不同的是先馆,
ImageLoader
加載圖片會(huì)先顯示默認(rèn)的圖片发框,等待圖片加載完成才會(huì)顯示在ImageView
上。
使用步驟
- 創(chuàng)建
RequestQueue
對(duì)象煤墙。 - 創(chuàng)建
ImageLoader
對(duì)象梅惯。 - 獲取
ImageListener
對(duì)象。 - 調(diào)用
ImageLoader.get()
方法加載網(wǎng)絡(luò)上的圖片仿野。
實(shí)例:
private void useImageLoader() {
//簡(jiǎn)書(shū)頭像的url
String url = "https://upload.jianshu.io/users/upload_avatars/11024422/9960fc0a-0e86-4a1b-ba25-bca296e674c9.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300";
//創(chuàng)建ImageLoader
/**
* 參數(shù)說(shuō)明:
* 1. 請(qǐng)求隊(duì)列
* 2. ImageCache铣减,顯然想實(shí)現(xiàn)一個(gè)性能好的緩存,避免不了使用LruChcae
* 由于現(xiàn)在對(duì)LruCache不甚了解脚作,就放上一個(gè)空ImageCache
*/
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageLoader.ImageCache() {
@Override
public Bitmap getBitmap(String url) {
return null;
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
});
//創(chuàng)建ImageListener對(duì)象
/**
* 參數(shù)說(shuō)明:
* 1. 指定顯示圖片的ImageView控件
* 2. 加載過(guò)程中顯示的圖片
* 3. 加載失敗時(shí)顯示的圖片
*/
ImageLoader.ImageListener listener = ImageLoader.getImageListener(
mImageView,R.mipmap.load,R.mipmap.failure);
//加載圖片
imageLoader.get(url,listener);
/*
//重載方法葫哗,可對(duì)大小進(jìn)行限制
imageLoader.get(url, listener, 200, 200);
*/
}
具體實(shí)現(xiàn)見(jiàn)示例注釋即可,參數(shù)說(shuō)明很詳細(xì)球涛。
NetworkImageView
NetworkImageView
是一個(gè)自定義控制劣针,它是繼承自ImageView
的,具備ImageView
控件的所有功能亿扁,并且在原生的基礎(chǔ)之上加入了加載網(wǎng)絡(luò)圖片的功能捺典。
使用步驟:
- 創(chuàng)建
RequestQueue
對(duì)象。 - 創(chuàng)建
ImageLoader
對(duì)象从祝。 - 在布局文件中添加
NetworkImageView
控件襟己。 - 獲取控件的實(shí)例。
- 設(shè)置加載圖片地址牍陌。
實(shí)例:
private void useNetworkImageView() {
//url
String url = "https://upload.jianshu.io/users/upload_avatars/11024422/9960fc0a-0e86-4a1b-ba25-bca296e674c9.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300";
//創(chuàng)建ImageLoader擎浴,參數(shù)說(shuō)明不在重復(fù)
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageLoader.ImageCache() {
@Override
public Bitmap getBitmap(String url) {
return null;
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
});
//下面兩個(gè)方法同ImageLoader.ImageListener listener = ImageLoader.getImageListener(mImageView,R.mipmap.load,R.mipmap.failure);
//作用是設(shè)置加載過(guò)程中和加載失敗時(shí)顯示的圖片
mNetworkImageView.setDefaultImageResId(R.mipmap.load);
mNetworkImageView.setErrorImageResId(R.mipmap.failure);
//設(shè)置加載的圖片地址
mNetworkImageView.setImageUrl(url,imageLoader);
}
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/network_image_view"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"/>
</LinearLayout>
使用方法和具體實(shí)現(xiàn)和ImageLoader
基本一致,不過(guò)需要注意的是:
NetworkImageView
并沒(méi)有提供設(shè)置最大寬度和高度的方法毒涧,根據(jù)我們?cè)O(shè)置控件的寬和高結(jié)合網(wǎng)絡(luò)圖片的寬和高內(nèi)部會(huì)自動(dòng)去實(shí)現(xiàn)壓縮贮预,呈現(xiàn)給我們一張大小剛剛好的網(wǎng)絡(luò)圖片,不占用任何一點(diǎn)多于內(nèi)存,如果我們不想要壓縮可以設(shè)置NetworkImageView
控件的寬和高都為wrap_content
萌狂。
GSONRequest
前面我們提到過(guò)JsonRequest
档玻,其返回結(jié)果是亂糟糟的JSON
數(shù)據(jù),解析起來(lái)太麻煩了茫藏,那么有沒(méi)有什么辦法可以讓JSON
數(shù)據(jù)解析變得簡(jiǎn)單呢答案當(dāng)然是肯定的误趴,我們可以借助GSON
這個(gè)工具,遺憾的是Volley
本身對(duì)此并不支持务傲,下面我們就來(lái)自己動(dòng)手寫(xiě)一個(gè)GSONRequest
凉当。
首先我們需要添加GSON依賴(lài)
implementation 'com.google.code.gson:gson:2.8.2'
我們分析一下GSONRequest如何實(shí)現(xiàn)
我們的目的是要更簡(jiǎn)單的解析JSON
數(shù)據(jù),為此我們引入了GSON
工具售葡,那么我們就可以從JsonRequest
入手看杭,而JsonRequest
、StringRequest
挟伙、ImageRequest
同繼承自Request
類(lèi)楼雹,因此我們著手分析最基礎(chǔ)的StringRequest
類(lèi),下面放上其源碼:
/**
* A canned request for retrieving the response body at a given URL as a String.
*/
public class StringRequest extends Request<String> {
/** Lock to guard mListener as it is cleared on cancel() and read on delivery. */
private final Object mLock = new Object();
// @GuardedBy("mLock")
private Listener<String> mListener;
/**
* Creates a new request with the given method.
*
* @param method the request {@link Method} to use
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/**
* Creates a new GET request.
*
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
public void cancel() {
super.cancel();
synchronized (mLock) {
mListener = null;
}
}
@Override
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
對(duì)源碼進(jìn)行分析:
-
Request
可以指定泛型 -
StringRequest
持有一個(gè)mListener
尖阔,用來(lái)接收響應(yīng)成功的回調(diào) -
mLock
用來(lái)維護(hù)mListener
- 提供兩個(gè)有參的構(gòu)造函數(shù)贮缅,前面我們不止一次提到參數(shù)的意義了,不再贅述
構(gòu)造函數(shù)必須調(diào)用
super()
介却,因?yàn)檎?qǐng)求和響應(yīng)在父類(lèi)中自動(dòng)處理
- 抽象方法
deliverResponse()
谴供,調(diào)用mListener
中的onResponse()
方法,傳入response
內(nèi)容齿坷,這樣我們就可以操作網(wǎng)絡(luò)響應(yīng)的數(shù)據(jù)了 - 抽象方法
parseNetworkResponse()
桂肌,對(duì)服務(wù)器相應(yīng)的數(shù)據(jù)解析,字節(jié)形式存入response.data
永淌,之后在取出數(shù)據(jù)組裝成String
傳入Response
的success
方法崎场。
下面我們就可以開(kāi)始編寫(xiě)我們的GsonRequest
了,示例如下:
public class GsonRequest<T> extends Request<T> {
private Gson mGson;
private Class<T> mClass;
//private final Map<String, String> headers;
private final Response.Listener<T> mListener;
public GsonRequest(int method, String url, Class<T> mClass, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.mClass = mClass;
this.mListener = listener;
mGson = new Gson();
}
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 json = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(mGson.fromJson(json,mClass),
HttpHeaderParser.parseCacheHeaders(response));
}catch (Exception e){
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
}
沒(méi)有什么太困難的仰禀,照貓畫(huà)虎即可照雁,除了設(shè)置泛型以外,核心是通過(guò)調(diào)用GSON
的fromJson()方
法將數(shù)據(jù)組成JavaBean
對(duì)象答恶,之后通過(guò)deliverResponse()
方法進(jìn)行回調(diào)。
JSON數(shù)據(jù)示例:
API地址https://www.sojson.com/open/api/weather/json.shtml?city=北京
{
"date": "20180404",
"message": "Success !",
"status": 200,
"city": "北京",
"count": 1365,
"data": {
"shidu": "85%",
"pm25": 14.0,
"pm10": 0.0,
"quality": "優(yōu)",
"wendu": "2",
"ganmao": "各類(lèi)人群可自由活動(dòng)",
"yesterday": {
"date": "03日星期二",
"sunrise": "05:58",
"high": "高溫 13.0℃",
"low": "低溫 4.0℃",
"sunset": "18:40",
"aqi": 88.0,
"fx": "北風(fēng)",
"fl": "3-4級(jí)",
"type": "陰",
"notice": "不要被陰云遮擋住好心情"
},
"forecast": [{
"date": "04日星期三",
"sunrise": "05:57",
"high": "高溫 10.0℃",
"low": "低溫 1.0℃",
"sunset": "18:41",
"aqi": 39.0,
"fx": "東北風(fēng)",
"fl": "<3級(jí)",
"type": "雨夾雪",
"notice": "道路濕滑萍诱,步行開(kāi)車(chē)要謹(jǐn)慎"
}, {
"date": "05日星期四",
"sunrise": "05:55",
"high": "高溫 11.0℃",
"low": "低溫 1.0℃",
"sunset": "18:42",
"aqi": 62.0,
"fx": "西南風(fēng)",
"fl": "<3級(jí)",
"type": "多云",
"notice": "陰晴之間悬嗓,謹(jǐn)防紫外線侵?jǐn)_"
}, {
"date": "06日星期五",
"sunrise": "05:53",
"high": "高溫 12.0℃",
"low": "低溫 4.0℃",
"sunset": "18:43",
"aqi": 56.0,
"fx": "西北風(fēng)",
"fl": "3-4級(jí)",
"type": "多云",
"notice": "陰晴之間,謹(jǐn)防紫外線侵?jǐn)_"
}, {
"date": "07日星期六",
"sunrise": "05:52",
"high": "高溫 14.0℃",
"low": "低溫 3.0℃",
"sunset": "18:44",
"aqi": 48.0,
"fx": "西北風(fēng)",
"fl": "3-4級(jí)",
"type": "晴",
"notice": "愿你擁有比陽(yáng)光明媚的心情"
}, {
"date": "08日星期日",
"sunrise": "05:50",
"high": "高溫 16.0℃",
"low": "低溫 5.0℃",
"sunset": "18:45",
"aqi": 67.0,
"fx": "南風(fēng)",
"fl": "<3級(jí)",
"type": "多云",
"notice": "陰晴之間裕坊,謹(jǐn)防紫外線侵?jǐn)_"
}]
}
}
對(duì)應(yīng)JavaBean類(lèi)包竹,強(qiáng)烈建議使用GsonFormat插件自動(dòng)生成
/GsonFormat對(duì)應(yīng)的JavaBean類(lèi)
public class Weather {
/**
* date : 20180404
* message : Success !
* status : 200
* city : 北京
* count : 1026
* data : {"shidu":"85%","pm25":14,"pm10":0,"quality":"優(yōu)","wendu":"2","ganmao":"各類(lèi)人群可自由活動(dòng)","yesterday":{"date":"03日星期二","sunrise":"05:58","high":"高溫 13.0℃","low":"低溫 4.0℃","sunset":"18:40","aqi":88,"fx":"北風(fēng)","fl":"3-4級(jí)","type":"陰","notice":"不要被陰云遮擋住好心情"},"forecast":[{"date":"04日星期三","sunrise":"05:57","high":"高溫 10.0℃","low":"低溫 1.0℃","sunset":"18:41","aqi":39,"fx":"東北風(fēng)","fl":"<3級(jí)","type":"雨夾雪","notice":"道路濕滑,步行開(kāi)車(chē)要謹(jǐn)慎"},{"date":"05日星期四","sunrise":"05:55","high":"高溫 11.0℃","low":"低溫 1.0℃","sunset":"18:42","aqi":62,"fx":"西南風(fēng)","fl":"<3級(jí)","type":"多云","notice":"陰晴之間,謹(jǐn)防紫外線侵?jǐn)_"},{"date":"06日星期五","sunrise":"05:53","high":"高溫 12.0℃","low":"低溫 4.0℃","sunset":"18:43","aqi":56,"fx":"西北風(fēng)","fl":"3-4級(jí)","type":"多云","notice":"陰晴之間周瞎,謹(jǐn)防紫外線侵?jǐn)_"},{"date":"07日星期六","sunrise":"05:52","high":"高溫 14.0℃","low":"低溫 3.0℃","sunset":"18:44","aqi":48,"fx":"西北風(fēng)","fl":"3-4級(jí)","type":"晴","notice":"愿你擁有比陽(yáng)光明媚的心情"},{"date":"08日星期日","sunrise":"05:50","high":"高溫 16.0℃","low":"低溫 5.0℃","sunset":"18:45","aqi":67,"fx":"南風(fēng)","fl":"<3級(jí)","type":"多云","notice":"陰晴之間苗缩,謹(jǐn)防紫外線侵?jǐn)_"}]}
*/
private String date;
private String message;
private int status;
private String city;
private int count;
private DataBean data;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public DataBean getData() {
return data;
}
public void setData(DataBean data) {
this.data = data;
}
public static class DataBean {
/**
* shidu : 85%
* pm25 : 14.0
* pm10 : 0.0
* quality : 優(yōu)
* wendu : 2
* ganmao : 各類(lèi)人群可自由活動(dòng)
* yesterday : {"date":"03日星期二","sunrise":"05:58","high":"高溫 13.0℃","low":"低溫 4.0℃","sunset":"18:40","aqi":88,"fx":"北風(fēng)","fl":"3-4級(jí)","type":"陰","notice":"不要被陰云遮擋住好心情"}
* forecast : [{"date":"04日星期三","sunrise":"05:57","high":"高溫 10.0℃","low":"低溫 1.0℃","sunset":"18:41","aqi":39,"fx":"東北風(fēng)","fl":"<3級(jí)","type":"雨夾雪","notice":"道路濕滑,步行開(kāi)車(chē)要謹(jǐn)慎"},{"date":"05日星期四","sunrise":"05:55","high":"高溫 11.0℃","low":"低溫 1.0℃","sunset":"18:42","aqi":62,"fx":"西南風(fēng)","fl":"<3級(jí)","type":"多云","notice":"陰晴之間声诸,謹(jǐn)防紫外線侵?jǐn)_"},{"date":"06日星期五","sunrise":"05:53","high":"高溫 12.0℃","low":"低溫 4.0℃","sunset":"18:43","aqi":56,"fx":"西北風(fēng)","fl":"3-4級(jí)","type":"多云","notice":"陰晴之間酱讶,謹(jǐn)防紫外線侵?jǐn)_"},{"date":"07日星期六","sunrise":"05:52","high":"高溫 14.0℃","low":"低溫 3.0℃","sunset":"18:44","aqi":48,"fx":"西北風(fēng)","fl":"3-4級(jí)","type":"晴","notice":"愿你擁有比陽(yáng)光明媚的心情"},{"date":"08日星期日","sunrise":"05:50","high":"高溫 16.0℃","low":"低溫 5.0℃","sunset":"18:45","aqi":67,"fx":"南風(fēng)","fl":"<3級(jí)","type":"多云","notice":"陰晴之間,謹(jǐn)防紫外線侵?jǐn)_"}]
*/
private String shidu;
private double pm25;
private double pm10;
private String quality;
private String wendu;
private String ganmao;
private YesterdayBean yesterday;
private List<ForecastBean> forecast;
public String getShidu() {
return shidu;
}
public void setShidu(String shidu) {
this.shidu = shidu;
}
public double getPm25() {
return pm25;
}
public void setPm25(double pm25) {
this.pm25 = pm25;
}
public double getPm10() {
return pm10;
}
public void setPm10(double pm10) {
this.pm10 = pm10;
}
public String getQuality() {
return quality;
}
public void setQuality(String quality) {
this.quality = quality;
}
public String getWendu() {
return wendu;
}
public void setWendu(String wendu) {
this.wendu = wendu;
}
public String getGanmao() {
return ganmao;
}
public void setGanmao(String ganmao) {
this.ganmao = ganmao;
}
public YesterdayBean getYesterday() {
return yesterday;
}
public void setYesterday(YesterdayBean yesterday) {
this.yesterday = yesterday;
}
public List<ForecastBean> getForecast() {
return forecast;
}
public void setForecast(List<ForecastBean> forecast) {
this.forecast = forecast;
}
public static class YesterdayBean {
/**
* date : 03日星期二
* sunrise : 05:58
* high : 高溫 13.0℃
* low : 低溫 4.0℃
* sunset : 18:40
* aqi : 88.0
* fx : 北風(fēng)
* fl : 3-4級(jí)
* type : 陰
* notice : 不要被陰云遮擋住好心情
*/
private String date;
private String sunrise;
private String high;
private String low;
private String sunset;
private double aqi;
private String fx;
private String fl;
private String type;
private String notice;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getHigh() {
return high;
}
public void setHigh(String high) {
this.high = high;
}
public String getLow() {
return low;
}
public void setLow(String low) {
this.low = low;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
public double getAqi() {
return aqi;
}
public void setAqi(double aqi) {
this.aqi = aqi;
}
public String getFx() {
return fx;
}
public void setFx(String fx) {
this.fx = fx;
}
public String getFl() {
return fl;
}
public void setFl(String fl) {
this.fl = fl;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
}
public static class ForecastBean {
/**
* date : 04日星期三
* sunrise : 05:57
* high : 高溫 10.0℃
* low : 低溫 1.0℃
* sunset : 18:41
* aqi : 39.0
* fx : 東北風(fēng)
* fl : <3級(jí)
* type : 雨夾雪
* notice : 道路濕滑彼乌,步行開(kāi)車(chē)要謹(jǐn)慎
*/
private String date;
private String sunrise;
private String high;
private String low;
private String sunset;
private double aqi;
private String fx;
private String fl;
private String type;
private String notice;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getHigh() {
return high;
}
public void setHigh(String high) {
this.high = high;
}
public String getLow() {
return low;
}
public void setLow(String low) {
this.low = low;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
public double getAqi() {
return aqi;
}
public void setAqi(double aqi) {
this.aqi = aqi;
}
public String getFx() {
return fx;
}
public void setFx(String fx) {
this.fx = fx;
}
public String getFl() {
return fl;
}
public void setFl(String fl) {
this.fl = fl;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
}
}
}
具體解析調(diào)用:
private void useGSONRequest() {
String url = "https://www.sojson.com/open/api/weather/json.shtml?city=北京";
GsonRequest<Weather> gsonRequest = new GsonRequest<Weather>(url, Weather.class,
new Response.Listener<Weather>() {
@Override
public void onResponse(Weather response) {
Weather.DataBean dataBean = response.getData();
List<Weather.DataBean.ForecastBean> forecastList = dataBean.getForecast();
Weather.DataBean.YesterdayBean yesterday = dataBean.getYesterday();
StringBuilder builder = new StringBuilder();
builder.append("日期:"+ response.getDate() + "\n");
builder.append("城市:"+ response.getCity() + "\n" + "\n");
builder.append("當(dāng)前天氣" + "\n");
builder.append("濕度: "+dataBean.getShidu()+"\n");
builder.append("PM25: "+dataBean.getPm25()+"\n");
builder.append("PM10: "+dataBean.getPm10()+"\n");
builder.append("空氣質(zhì)量: "+dataBean.getQuality()+"\n");
builder.append("溫度: "+dataBean.getWendu()+"\n");
builder.append("感冒指數(shù): "+dataBean.getGanmao()+"\n"+ "\n");
builder.append("昨日天氣" + "\n");
builder.append("日期: "+yesterday.getDate()+"\n");
builder.append("日出時(shí)間: "+yesterday.getSunrise()+"\n");
builder.append("最高溫度: "+yesterday.getHigh()+"\n");
builder.append("最低溫度: "+yesterday.getLow()+"\n");
builder.append("日落時(shí)間: "+yesterday.getSunset()+"\n");
builder.append("AQI: "+yesterday.getAqi()+"\n");
builder.append("風(fēng)向: "+yesterday.getFx()+"\n");
builder.append("風(fēng)力: "+yesterday.getFl()+"\n");
builder.append("天氣: "+yesterday.getType()+"\n");
builder.append("注意: "+yesterday.getNotice()+"\n"+ "\n");
for (Weather.DataBean.ForecastBean forecast : forecastList){
builder.append("未來(lái)天氣:" + "\n");
builder.append("日期: "+forecast.getDate()+"\n");
builder.append("日出時(shí)間: "+forecast.getSunrise()+"\n");
builder.append("最高溫度: "+forecast.getHigh()+"\n");
builder.append("最低溫度: "+forecast.getLow()+"\n");
builder.append("日落時(shí)間: "+forecast.getSunset()+"\n");
builder.append("AQI: "+forecast.getAqi()+"\n");
builder.append("風(fēng)向: "+forecast.getFx()+"\n");
builder.append("風(fēng)力: "+forecast.getFl()+"\n");
builder.append("天氣: "+forecast.getType()+"\n");
builder.append("注意: "+forecast.getNotice()+"\n"+ "\n");
}
mTextView.setText(builder);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText(error.getMessage());
}
});
mQueue.add(gsonRequest);
}
XMLRequest
了解上面的原理之后泻肯,我們可以很容易的編寫(xiě)XMLRequest
實(shí)例:
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 parser = factory.newPullParser();
parser.setInput(new StringReader(xmlString));
return Response.success(parser,HttpHeaderParser.parseCacheHeaders(response));
}catch (Exception e){
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(XmlPullParser response) {
mListener.onResponse(response);
}
}
我們通過(guò)PULL
方式進(jìn)行解析,因此最后返回一個(gè)XmlPullParser
對(duì)象慰照。
XML文件示例
API地址https://www.sojson.com/open/api/weather/xml.shtml?city=北京
<resp>
<city>北京</city>
<updatetime>23:31</updatetime>
<wendu>3</wendu>
<fengli>
<![CDATA[ 2級(jí) ]]>
</fengli>
<shidu>85%</shidu>
<fengxiang>東風(fēng)</fengxiang>
<sunrise_1>05:57</sunrise_1>
<sunset_1>18:41</sunset_1>
<sunrise_2/>
<sunset_2/>
<environment>
<aqi>34</aqi>
<pm25>13</pm25>
<suggest>各類(lèi)人群可自由活動(dòng)</suggest>
<quality>優(yōu)</quality>
<MajorPollutants/>
<o3>44</o3>
<co>0</co>
<pm10>10</pm10>
<so2>2</so2>
<no2>27</no2>
<time>23:00:00</time>
</environment>
<yesterday>
<date_1>2日星期一</date_1>
<high_1>高溫 26℃</high_1>
<low_1>低溫 11℃</low_1>
<day_1>
<type_1>多云</type_1>
<fx_1>東北風(fēng)</fx_1>
<fl_1>
<![CDATA[ <3級(jí) ]]>
</fl_1>
</day_1>
<night_1>
<type_1>多云</type_1>
<fx_1>東北風(fēng)</fx_1>
<fl_1>
<![CDATA[ 3-4級(jí) ]]>
</fl_1>
</night_1>
</yesterday>
<forecast>
<weather>
<date>3日星期二</date>
<high>高溫 13℃</high>
<low>低溫 2℃</low>
<day>
<type>陰</type>
<fengxiang>北風(fēng)</fengxiang>
<fengli>
<![CDATA[ 3-4級(jí) ]]>
</fengli>
</day>
<night>
<type>陰</type>
<fengxiang>東北風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</night>
</weather>
<weather>
<date>4日星期三</date>
<high>高溫 10℃</high>
<low>低溫 1℃</low>
<day>
<type>雨夾雪</type>
<fengxiang>東北風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</day>
<night>
<type>雨夾雪</type>
<fengxiang>北風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</night>
</weather>
<weather>
<date>5日星期四</date>
<high>高溫 11℃</high>
<low>低溫 1℃</low>
<day>
<type>多云</type>
<fengxiang>西南風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</day>
<night>
<type>多云</type>
<fengxiang>西風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</night>
</weather>
<weather>
<date>6日星期五</date>
<high>高溫 12℃</high>
<low>低溫 4℃</low>
<day>
<type>多云</type>
<fengxiang>西北風(fēng)</fengxiang>
<fengli>
<![CDATA[ 3-4級(jí) ]]>
</fengli>
</day>
<night>
<type>多云</type>
<fengxiang>西北風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</night>
</weather>
<weather>
<date>7日星期六</date>
<high>高溫 14℃</high>
<low>低溫 3℃</low>
<day>
<type>晴</type>
<fengxiang>西北風(fēng)</fengxiang>
<fengli>
<![CDATA[ 3-4級(jí) ]]>
</fengli>
</day>
<night>
<type>多云</type>
<fengxiang>東北風(fēng)</fengxiang>
<fengli>
<![CDATA[ <3級(jí) ]]>
</fengli>
</night>
</weather>
</forecast>
<zhishus>
<zhishu>
<name>晨練指數(shù)</name>
<value>較不宜</value>
<detail>有降水灶挟,較不宜晨練,室外鍛煉請(qǐng)攜帶雨具毒租。建議年老體弱人群適當(dāng)減少晨練時(shí)間稚铣。</detail>
</zhishu>
<zhishu>
<name>舒適度</name>
<value>較舒適</value>
<detail>白天會(huì)有降雪,這種天氣條件下墅垮,人們會(huì)感到有些涼意榛泛,但大部分人完全可以接受。</detail>
</zhishu>
<zhishu>
<name>穿衣指數(shù)</name>
<value>較冷</value>
<detail>建議著厚外套加毛衣等服裝噩斟。年老體弱者宜著大衣曹锨、呢外套加羊毛衫。</detail>
</zhishu>
<zhishu>
<name>感冒指數(shù)</name>
<value>較易發(fā)</value>
<detail>天氣較涼剃允,較易發(fā)生感冒沛简,請(qǐng)適當(dāng)增加衣服。體質(zhì)較弱的朋友尤其應(yīng)該注意防護(hù)斥废。</detail>
</zhishu>
<zhishu>
<name>晾曬指數(shù)</name>
<value>不宜</value>
<detail>有降雪椒楣,不適宜晾曬。若需要晾曬牡肉,請(qǐng)?jiān)谑覂?nèi)準(zhǔn)備出充足的空間捧灰。</detail>
</zhishu>
<zhishu>
<name>旅游指數(shù)</name>
<value>適宜</value>
<detail>溫度適宜,同時(shí)又有微風(fēng)伴您一路同行统锤。比較適宜旅游煤篙,但有降雪,出行請(qǐng)注意攜帶雨具厢岂。</detail>
</zhishu>
<zhishu>
<name>紫外線強(qiáng)度</name>
<value>最弱</value>
<detail>屬弱紫外線輻射天氣捆交,無(wú)需特別防護(hù)。若長(zhǎng)期在戶外逾雄,建議涂擦SPF在8-12之間的防曬護(hù)膚品阀溶。</detail>
</zhishu>
<zhishu>
<name>洗車(chē)指數(shù)</name>
<value>不宜</value>
<detail>不宜洗車(chē)腻脏,未來(lái)24小時(shí)內(nèi)有雨,如果在此期間洗車(chē)银锻,雨水和路上的泥水可能會(huì)再次弄臟您的愛(ài)車(chē)永品。</detail>
</zhishu>
<zhishu>
<name>運(yùn)動(dòng)指數(shù)</name>
<value>較不宜</value>
<detail>有降雪,推薦您在室內(nèi)進(jìn)行低強(qiáng)度運(yùn)動(dòng)击纬;若堅(jiān)持戶外運(yùn)動(dòng)鼎姐,請(qǐng)選擇合適運(yùn)動(dòng)并注意保暖。</detail>
</zhishu>
<zhishu>
<name>約會(huì)指數(shù)</name>
<value>較不適宜</value>
<detail>室外有風(fēng)掉弛,而且有降雪症见,會(huì)給室外約會(huì)帶來(lái)不便,如果外出約會(huì)殃饿,請(qǐng)一定做好準(zhǔn)備谋作。</detail>
</zhishu>
<zhishu>
<name>雨傘指數(shù)</name>
<value>帶傘</value>
<detail>將有降雪,您在外出的時(shí)候一定要帶雨傘乎芳,以免弄濕衣物著涼遵蚜。</detail>
</zhishu>
</zhishus>
</resp>
代碼中具體解析示例
private void useXMLRequest() {
String url = "https://www.sojson.com/open/api/weather/xml.shtml?city=%E5%8C%97%E4%BA%AC";
XMLRequest xmlRequest = new XMLRequest(url, 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 name = response.getName();
if ("city".equals(name)){
mTextView.setText(response.nextText()); }
break;
}
eventType = response.next();
}
}catch (Exception e){
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mTextView.setText(error.getMessage());
}
});
mQueue.add(xmlRequest);
}
偷個(gè)懶,我就不具體解析這段XML
了(笑~)奈惑,大家可以自己練練手吭净,熟悉XMLRequest
的同時(shí)也可以復(fù)習(xí)以下基于PULL
方式的XML
解析。
總結(jié)
- 本文詳細(xì)介紹了Volley肴甸,包括簡(jiǎn)單請(qǐng)求寂殉、圖片請(qǐng)求以及自定義請(qǐng)求的使用。
- 筆者水平有限原在,如有錯(cuò)漏友扰,歡迎指正。
- 接下來(lái)我將持續(xù)推出Android網(wǎng)絡(luò)相關(guān)的一系列文章庶柿,包括HttpURLConnection村怪、Volley、OkHttp3浮庐、Retrofit2的使用等甚负,有興趣可以關(guān)注whd_Alive的Android開(kāi)發(fā)筆記
歡迎關(guān)注whd_Alive的簡(jiǎn)書(shū)
- 不定期分享Android開(kāi)發(fā)相關(guān)的技術(shù)干貨,期待與你的交流审残,共勉梭域。