一、前言:
Volley是Google官方出的一套小而巧的異步請(qǐng)求庫(kù),該框架封裝的擴(kuò)展性很強(qiáng),支持HttpClient、HttpUrlConnection,甚至支持OkHttp,而且Volley里面也封裝了ImageLoader,所以如果你愿意你甚至不需要使用圖片加載框架,不過這塊功能沒有一些專門的圖片加載框架強(qiáng)大,對(duì)于簡(jiǎn)單的需求可以使用,對(duì)于稍復(fù)雜點(diǎn)的需求還是需要用到專門的圖片加載框架。
Volley也有缺陷,比如不支持post大數(shù)據(jù),所以不適合上傳文件。不過Volley設(shè)計(jì)的初衷本身也就是為頻繁的、數(shù)據(jù)量小的網(wǎng)絡(luò)請(qǐng)求而生!
1. Volley網(wǎng)絡(luò)請(qǐng)求隊(duì)列
Volley 請(qǐng)求網(wǎng)絡(luò)都是基于請(qǐng)求隊(duì)列的,開發(fā)者只要把請(qǐng)求放在請(qǐng)求隊(duì)列中就可以了,請(qǐng)求隊(duì)列會(huì)依次進(jìn)行請(qǐng)求。一般情況下,一個(gè)應(yīng)用程序如果網(wǎng)絡(luò)請(qǐng)求不是特別的頻繁,完全可以只有一個(gè)請(qǐng)求隊(duì)列(對(duì)應(yīng) Application);如果網(wǎng)絡(luò)請(qǐng)求非常多或者有其他情況,則可以是一個(gè) Activity 對(duì)應(yīng)一個(gè)網(wǎng)絡(luò)請(qǐng)求隊(duì)列,這就要看具體情況了。
示例:
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
網(wǎng)絡(luò)請(qǐng)求權(quán)限一定不要忘記:
<uses-permission android:name="android.permission.INTERNET"/>
二、 基本使用:
1.volley依賴
//volley依賴
implementation 'com.mcxiaoke.volley:library:1.0.19'
//gson依賴
compile 'com.google.code.gson:gson:2.7'
//butterknife,配置下面 兩行
implementation 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
2.RequestQueue在 Application 中初始化一次
public class MyApplication extends Application {
public static RequestQueue queue;
@Override
public void onCreate() {
super.onCreate();
queue = Volley.newRequestQueue(getApplicationContext());
}
/**
* 提供全局變量,如果使用 VolleyUtils 類,這里就不用寫了
* 創(chuàng)建請(qǐng)求隊(duì)列對(duì)象,通常情況下,一個(gè)類中(或者說一個(gè)工程中)保持 始終使用同一個(gè)RequestQueue對(duì)象即可
* @return
*/
public static RequestQueue getHttpQueues() {
return queue;
}
}
3. StringRequest 中GET 請(qǐng)求
/**
* get 請(qǐng)求
*/
private void getRequest() {
String url = "http://apis.juhe.cn/mobile/get?phone=18856907654&key=5778e9d9cf089fc3b093b162036fc0e1";
/**
* 1. int 類型 用于指定請(qǐng)求的方式(如GET或者POST)
* 2. String類型 用于指定網(wǎng)絡(luò)請(qǐng)求要連接的網(wǎng)址
* 3. Listener類型 ,接收網(wǎng)絡(luò)響應(yīng)的接口,即只要得到本次請(qǐng)求對(duì)應(yīng)的返回結(jié)果
* 就會(huì)運(yùn)行此接口中的onResponse方法
* 4: ErrorListener類型, 用于接收當(dāng)網(wǎng)絡(luò)請(qǐng)求的過程中一旦發(fā)生了什么錯(cuò)誤,
* 就會(huì)調(diào)用本接口中的onErrorResponse方法
* */
StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("LUO", "========" + response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("LUO", "========" + error.getMessage());
}
});
//三,給請(qǐng)求對(duì)象設(shè)置tag標(biāo)識(shí)
stringRequest.setTag("get");
//四,將請(qǐng)求添加到請(qǐng)求隊(duì)列中,執(zhí)行網(wǎng)絡(luò)請(qǐng)求
MyApplication.getHttpQueues().add(stringRequest);
//不要調(diào)用,看下方注意
//MyApplication.getHttpQueues().start();
}
注意:
//MyApplication.getHttpQueues().start();
建議不要手動(dòng)調(diào)用 RequestQueue 的 start() 方法,引起的問題“經(jīng)常會(huì)報(bào)com.android.volley.NoConnectionError: java.io.InterruptedIOException”,然后就內(nèi)容加載失敗。。。
因?yàn)樵?Volley.newRequestQueue() 方法中,已經(jīng)調(diào)用了 start() 方法。
看源碼示例:
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//這里已經(jīng)啟動(dòng)了,不需要請(qǐng)求中再次調(diào)用
queue.start();
return queue;
}
3. StringRequest 中 POST請(qǐng)求
/**
* post 請(qǐng)求
*/
private void postRequest() {
RequestQueue queue = Volley.newRequestQueue(this);
String postUrl = "http://apis.juhe.cn/mobile/get";
StringRequest stringRequest = new StringRequest(StringRequest.Method.POST, postUrl, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("LUO", "========" + response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("LUO", "========" + error.getMessage());
}
}) {
/**
* Post請(qǐng)求和Get請(qǐng)求的使用步驟上的區(qū)別在于請(qǐng)求條件的指定
* 必須在StringRequest對(duì)象的后面添加{},并且
* 在{}內(nèi)重寫getParams方法,該方法的返回值就是所有的請(qǐng)求條件
* */
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//將請(qǐng)求條件封裝到map對(duì)象中
Map<String, String> map = new HashMap<>();
map.put("phone", "18856907654");
map.put("key", "5778e9d9cf089fc3b093b162036fc0e1");
return map;
}
};
//三,給請(qǐng)求對(duì)象設(shè)置tag標(biāo)識(shí)
stringRequest.setTag("get");
//四,將請(qǐng)求添加到請(qǐng)求隊(duì)列中,執(zhí)行網(wǎng)絡(luò)請(qǐng)求
MyApplication.getHttpQueues().add(stringRequest);
}
4.JSONObject網(wǎng)絡(luò)請(qǐng)求
private void jsonRequest() {
String postUrl = "http://apis.juhe.cn/mobile/get";
Map<String, String> map = new HashMap<>();
map.put("phone", "18856907654");
map.put("key", "5778e9d9cf089fc3b093b162036fc0e1");
String json = new Gson().toJson(map);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
postUrl,json, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("LUO", "========" + response);
}
},new Response.ErrorListener(){
@Override
public void onErrorResponse(VolleyError error) {
Log.d("LUO", "========" + error.getMessage());
}
});
MyApplication.getHttpQueues().add(jsonObjectRequest);
}
5.下載單張圖片
/**
* 下載單張圖片
*/
private void singlePhoto() {
ImageRequest imageRequest = new ImageRequest(
"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2019270811,1269730008&fm=27&gp=0.jpg", //對(duì)應(yīng)圖片的下載地址
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
Log.d("LUO", "========" + response);
ivIcon.setImageBitmap(response);
}
}, 200, 200,// 指定下載后圖片的最大寬高
ImageView.ScaleType.FIT_XY,//指定圖片的縮放模式
Bitmap.Config.ARGB_8888,//指定圖片的編碼格式
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("LUO", "========" + error.getMessage());
}
});
MyApplication.getHttpQueues().add(imageRequest);
}
6.與Avctivity生命周期聯(lián)動(dòng)(注意啊)
其實(shí)就是在Activity退出時(shí)候或銷毀時(shí)候,取消對(duì)應(yīng)的網(wǎng)絡(luò)請(qǐng)求,避免網(wǎng)絡(luò)請(qǐng)求在后臺(tái)浪費(fèi)資源,所以,我們一般在onStop()方法中通過之前設(shè)置的Tag取消網(wǎng)絡(luò)請(qǐng)求:
@Override
protected void onDestroy() {
super.onDestroy();
//通過Tag標(biāo)簽取消請(qǐng)求隊(duì)列中對(duì)應(yīng)的全部請(qǐng)求
MyApplication.getHttpQueues().cancelAll(tag);
}
三、封裝使用:
1.VolleyUtils類
public class VolleyUtils {
private static RequestQueue mQueue;
private ImageLoader mLoader;
private ImageLoader.ImageCache mCache;
private static VolleyUtils mInstance;
/**
* 1.構(gòu)造方法私有化
* @param context
*/
private VolleyUtils(Context context) {
//做一些事情
mQueue = Volley.newRequestQueue(context);
mCache = new MyImageCache();
mLoader = new ImageLoader(mQueue, mCache);
}
public RequestQueue getQueue() {
return mQueue;
}
public ImageLoader getLoader() {
return mLoader;
}
/**
* 2.提供一個(gè)靜態(tài)方法,返回一個(gè)當(dāng)前類
* @param context
* @return
*/
public static VolleyUtils create(Context context) {
if (mInstance == null) {
synchronized (VolleyUtils.class) {
if (mInstance == null) {
mInstance = new VolleyUtils(context);
}
}
}
return mInstance;
}
public <T> void get(String url, final Class<T> clazz, final OnResponse<T> listener) {
HashMap<String, String> map = new HashMap<>();
listener.OnMap(map);
String param = prepareParam(map);
if (param.trim().length() >= 1) {
url += "?" + param;
}
Log.e("Volley", "urlResult---->" + url);
StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("Volley", "response-->" + response);
Gson gson = new Gson();
listener.onSuccess(gson.fromJson(response, clazz));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley", "response-->" + error.getMessage());
listener.onError(error.getMessage());
}
});
mQueue.add(stringRequest);
}
private static String prepareParam(Map<String, String> paramMap) {
StringBuilder sb = new StringBuilder();
if (paramMap.isEmpty()) {
return "";
} else {
for (String key : paramMap.keySet()) {
String value = paramMap.get(key);
if (sb.length() < 1) {
sb.append(key).append("=").append(value);
} else {
sb.append("&").append(key).append("=").append(value);
}
}
return sb.toString();
}
}
public <T> void post(String url, final Class<T> clazz, final OnResponse<T> listener) {
StringRequest stringRequest = new StringRequest(StringRequest.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("Volley", "response-->" + response);
Gson gson = new Gson();
listener.onSuccess(gson.fromJson(response, clazz));
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley", "response-->" + error.getMessage());
listener.onError(error.getMessage());
}
}) {
/**
* Post請(qǐng)求和Get請(qǐng)求的使用步驟上的區(qū)別在于請(qǐng)求條件的指定
* 必須在StringRequest對(duì)象的后面添加{},并且
* 在{}內(nèi)重寫getParams方法,該方法的返回值就是所有的請(qǐng)求條件
* */
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//將請(qǐng)求條件封裝到map對(duì)象中
Map<String, String> map = new HashMap<>();
listener.OnMap(map);
return map;
}
};
mQueue.add(stringRequest);
}
public void loadImg(String url, ImageView view, int maxWidth, int maxHeight, int defaultImageResId, int errorImageResId) {
mLoader.get(url, //圖片的下載路徑
/**
* 通過getImageListener方法獲取ImageListener接口對(duì)象
* 參數(shù)1: 圖片下載完成后,由哪個(gè)控件顯示圖片
* 參數(shù)2: 設(shè)置圖片下載過程中顯示的默認(rèn)圖片
* 參數(shù)3: 設(shè)置一旦圖片下載出錯(cuò),就顯示出錯(cuò)提示圖片
* */
ImageLoader.getImageListener(view, defaultImageResId, errorImageResId),
maxWidth, maxHeight, //圖片的最大寬高 指定成0的話就表示不管圖片有多大
ImageView.ScaleType.FIT_XY //圖片的縮放模式
);
}
public void loadImg(String url, ImageView view) {
mLoader.get(url, //圖片的下載路徑
ImageLoader.getImageListener(view, R.mipmap.ic_launcher, R.mipmap.ic_launcher),
0, 0, //圖片的最大寬高 指定成0的話就表示不管圖片有多大
ImageView.ScaleType.FIT_XY //圖片的縮放模式
);
}
public void loadImg(String url, ImageView view, int defaultImageResId, int errorImageResId) {
mLoader.get(url, //圖片的下載路徑
ImageLoader.getImageListener(view, defaultImageResId, errorImageResId),
0, 0, //圖片的最大寬高 指定成0的話就表示不管圖片有多大
ImageView.ScaleType.FIT_XY //圖片的縮放模式
);
}
/**
* 分配一定內(nèi)存空間,專門存取圖片,一般為內(nèi)存大小的1/8
*/
private class MyImageCache implements ImageLoader.ImageCache {
private LruCache<String, Bitmap> mCache;
private MyImageCache() {
//分配最大內(nèi)存空間的1/8
long maxMemory = Runtime.getRuntime().maxMemory() / 8;
mCache = new LruCache<String, Bitmap>((int) maxMemory) {
@Override
protected int sizeOf(String key, Bitmap value) {
//得到當(dāng)前圖片的大小
return value.getByteCount();
}
};
}
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
if (getBitmap(url) == null)
mCache.put(url, bitmap);
}
}
/**
* 自定義的類型
* @param <T>
*/
public class GsonRequest<T> extends Request<T> {
private Response.Listener<T> mListener;
private Gson mGson;
private Class<T> mClazz;
private GsonRequest(int method, String url, Response.Listener<T> listener, Response.ErrorListener errorListenerlistener, Class<T> clazz) {
super(method, url, errorListenerlistener);
this.mListener = listener;
mGson = new Gson();
this.mClazz = clazz;
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
Log.e("Volley", "服務(wù)器返回JSON------>" + parsed);
return Response.success(mGson.fromJson(parsed, mClazz), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new VolleyError(e));
}
}
@Override
protected void deliverResponse(T response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
@Override
protected void onFinish() {
super.onFinish();
mListener = null;
}
}
public interface OnResponse<T> {
void OnMap(Map<String, String> map);
void onSuccess(T response);
void onError(String error);
}
}
2.PhoneBean類
public class PhoneBean {
/**
* resultcode : 200
* reason : Return Successd!
* result : {"province":"安徽","city":"合肥","areacode":"0551","zip":"230000","company":"移動(dòng)","card":""}
* error_code : 0
*/
private String resultcode;
private String reason;
private ResultBean result;
private int error_code;
public String getResultcode() {
return resultcode;
}
public void setResultcode(String resultcode) {
this.resultcode = resultcode;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public ResultBean getResult() {
return result;
}
public void setResult(ResultBean result) {
this.result = result;
}
public int getError_code() {
return error_code;
}
public void setError_code(int error_code) {
this.error_code = error_code;
}
public static class ResultBean {
/**
* province : 安徽
* city : 合肥
* areacode : 0551
* zip : 230000
* company : 移動(dòng)
* card :
*/
private String province;
private String city;
private String areacode;
private String zip;
private String company;
private String card;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAreacode() {
return areacode;
}
public void setAreacode(String areacode) {
this.areacode = areacode;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getCard() {
return card;
}
public void setCard(String card) {
this.card = card;
}
}
}
3.直接調(diào)用:
public class TwoActivity extends Activity{
private ImageView img;
String urlStr = "http://apis.juhe.cn/mobile/get";
String imgUrl = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1263067444,118499721&fm=27&gp=0.jpg";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
img = findViewById(R.id.img);
textView = findViewById(R.id.text);
}
public void btnGet(View view) {
VolleyUtils.create(this)
.get(urlStr, PhoneBean.class, new VolleyUtils.OnResponse<PhoneBean>() {
@Override
public void OnMap(Map<String, String> map) {
map.put("phone", "18856907654");
map.put("key", "5778e9d9cf089fc3b093b162036fc0e1");
}
@Override
public void onSuccess(PhoneBean response) {
Log.e("TAG", "response---->" + response);
textView.setText("get :" + response.getResultcode() + " " + response.getResult().getCity());
}
@Override
public void onError(String error) {
Log.e("TAG", "error---->" + error);
textView.setText("error--->" + error);
}
});
}
public void btnPost(View view) {
VolleyUtils.create(this)
.post(urlStr, PhoneBean.class, new VolleyUtils.OnResponse<PhoneBean>() {
@Override
public void OnMap(Map<String, String> map) {
map.put("phone", "18856907654");
map.put("key", "5778e9d9cf089fc3b093b162036fc0e1");
}
@Override
public void onSuccess(PhoneBean response) {
Log.e("TAG", "response---->" + response);
textView.setText("post :" + response.getResultcode() + " " + response.getResult().getCity());
}
@Override
public void onError(String error) {
Log.e("TAG", "error---->" + error);
textView.setText("error--->" + error);
}
});
}
public void btnImg(View view) {
VolleyUtils.create(this)
.loadImg(imgUrl, img);
// VolleyUtils.create(this)
// .loadImg(imgUrl,img,200,200,R.mipmap.ic_launcher,R.mipmap.ic_launcher);
//
// VolleyUtils.create(this)
// .loadImg(imgUrl,img,R.mipmap.ic_launcher,R.mipmap.ic_launcher);
}
public static void launch(Context mContext) {
Intent intent = new Intent(mContext,TwoActivity.class);
mContext.startActivity(intent);
}
}
GitHub地址:
2.支持 HTTPS請(qǐng)求的地址:https://github.com/lyyRunning/VolleySSL