android volley解析與二次封裝
最近看到一本書,其中有一句話寫的不錯:實際上,將第三方API打包是個良好的實踐手段,當你打包一個第三方API,你就降低了對它的依賴,未來你可以不太痛苦地改用其他程式碼庫,在你測試自己的程式碼時,打包也有助於模擬第三方呼叫。打包的好處還在於你不必幫死在某個特定的API設計上。你可以定義自己感覺舒服的API。
現在我們公司的應用就有這個問題,老大不知道出於什麼考慮,使用的是AQuery框架進行的網路訪問和圖片載入,很多人可能沒有聽說過這個庫,因為github上顯示它已經在2013年停止更新了。那麼現在如果需要更新整個應用的框架成volley怎麼辦?答案是很難,非常難,整個應用和AQuery框架耦合非常嚴重,修改起來的難度和時間成本都不可估計。
基於以上上下文,Volley儘管已經很完善了,但是為了以後的考慮,還是需要將Volley進行簡單封裝,外部不能直接使用Volley相關類,只能使用通過封裝之後提供的api進行網路訪問和圖片載入。
在封裝一個api之前需要詳細瞭解一下這個api,網上的Volley介紹太多了,我列出幾個講的比較好的:
https://bxbxbai.github.io/2014/09/14/android-working-with-volley/ 這個主要是講了一下Volley的用法
http://code.tutsplus.com/tutorials/an-introduction-to-volley--cms-23800 這個介紹了Volley的一些實用功能,比如header,cookies和訪問優先順序
http://www.androidhive.info/2014/05/android-working-with-volley-library-1/ 這個部落格將Volley進行了簡單的封裝
看完這些部落格之後,應該對使用有了初步瞭解,下面是對Volley原始碼的詳細分析:
http://www.chengxuyuans.com/Android/90526.html
還有一個比較重要和實用的是Volley有一個判斷快取是否失效的功能,這個功能主要是通過Request類的parseNetworkResponse()方法來獲取網路資料的header HttpHeaderParser.parseCacheHeaders(response) 根據header裡面的資訊來判斷快取的失效 現在我的初步想法是有兩種方案,第一種是將Volley只進行簡單的封裝,方便使用,這種封裝方式的優點是可以使用Volley的所有功能,缺點是會造成整個專案和Volley第三方API的耦合程度很高,不方便以後的修改;第二種就是將Volley進行高程度封裝,外部不允許呼叫任何原生Volley的功能,只能使用封裝類提供的介面,這種方案優點是可以非常方便的修改框架,缺點就是無法完全使用Volley的各種功能,只能使用最基本的功能。 這是我的想法,不知道是不是錯的,小白一個,只能想到這種拙劣的解決方案,望指點。
有了這兩種方案,於是就開始著手寫程式碼封裝了
第一種方案: BaseVolleyApi.class類
public abstract class BaseVolleyApi {
private static RequestQueue requestQueue;
private static ImageLoader imageLoader;
public static RequestQueue getRequestQueue() {
if (requestQueue == null) {
synchronized (BaseVolleyApi.class){
if (requestQueue == null)
requestQueue = Volley.newRequestQueue(RootApplication.getInstance());
}
}
return requestQueue;
}
public static ImageLoader getImageLoader() {
if (imageLoader == null) {
synchronized (BaseVolleyApi.class) {
if (imageLoader == null){
VolleyLruCache cache = new VolleyLruCache();
imageLoader = new ImageLoader(getRequestQueue(), cache);
}
}
}
return imageLoader;
}
}
VolleyLruCache.class類
public class VolleyLruCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache{
private static int getCacheSize(){
return (int)(Runtime.getRuntime().maxMemory()/1024/8);
}
public VolleyLruCache() {
this(getCacheSize());
}
private VolleyLruCache(int size){
super(size);
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
}
上面的實現方案就是第一種,簡單封裝,方便使用,耦合度很大,不方便以後的框架修改
第二種方案:
第二種方案就比較複雜了,主要是要對Volley的功能進行抽取,選出最基本的功能進行封裝,這樣就能夠相對輕鬆的更換框架。這種方案的程式碼複雜度就比較高了,網路訪問和圖片載入就需要分成兩個單獨的類去處理。
BaseNetApi.class類
public abstract class BaseNetApi {
/** 網路訪問requestQueue */
private RequestQueue requestQueue;
private RequestQueue getRequestQueue(int maxDiskCacheBytes){
if (requestQueue == null)
requestQueue = Volley.newRequestQueue(RootApplication.getInstance(), maxDiskCacheBytes);
return requestQueue;
}
protected RequestQueue getRequestQueue(){
return getRequestQueue(-1);
}
/**
* 回撥介面
*/
public interface OnNetCallback<T>{
void onSuccess(T result);
void onFail(NetError error);
}
private boolean checkIfExtendsRequest(Class clazz){
while (clazz.getSuperclass() != null){
clazz = clazz.getSuperclass();
if (clazz == Request.class)
return true;
}
return false;
}
/**
* 網路請求
*/
protected <T> void makeRequest(final Context context, Class<?> clazz, String url, final Map<String, String> params, final OnNetCallback<T> callback){
//網路請求
Request request = null;
//失敗回撥
Response.ErrorListener errorListener = null;
//成功回撥
Response.Listener listener = null;
//判空
if (callback != null) {
errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (context instanceof Activity && (((Activity)(context)).isFinishing())) {
L.i("activity finish, not callback");
return ;
}
NetError netError = new NetError();
netError.transferVolleyError(error);
callback.onFail(netError);
}
};
listener = new Response.Listener<T>() {
@Override
public void onResponse(T response) {
if (context instanceof Activity && (((Activity)(context)).isFinishing())) {
L.i("activity finish, not callback");
return ;
}
callback.onSuccess(response);
}
};
}
//啟動網路請求
if (clazz == ImageRequest.class){
throw new IllegalArgumentException("please use imageloader");
}else if (checkIfExtendsRequest(clazz)) {
try {
Constructor constructor = clazz.getConstructor(int.class, String.class, Response.Listener.class,
Response.ErrorListener.class, Map.class);
int method = Request.Method.GET;
if (params != null)
method = Request.Method.POST;
request = (Request) constructor.newInstance(method, url, listener, errorListener, params);
} catch (Exception e) {
L.e("error reflect", e);
return;
}
}else {
throw new IllegalArgumentException("unsupported type");
}
//自定義超時時間,重試次數
// request.setRetryPolicy(new DefaultRetryPolicy());
getRequestQueue().add(request);
}
/**
* 對{@linkplain StringRequest}的封裝類
*/
private static class StringRequestImpl extends StringRequest{
private Map<String, String> params;
public StringRequestImpl(int method, String url, Response.Listener<String> listener,
Response.ErrorListener errorListener, Map<String, String> params) {
super(method, url, listener, errorListener);
this.params = params;
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params;
}
}
/**
* 對{@linkplain JsonObjectRequest}的封裝類
*/
private static class JsonObjectRequestImpl extends JsonObjectRequest{
private Map<String, String> params;
public JsonObjectRequestImpl(int method, String url, Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener, Map<String, String> params) {
super(method, url, listener, errorListener);
this.params = params;
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params;
}
}
/**
* 對{@linkplain JsonArrayRequest}的封裝類
*/
private static class JsonArrayRequestImpl extends JsonArrayRequest{
private Map<String, String> params;
public JsonArrayRequestImpl(int method, String url, Response.Listener<JSONArray> listener,
Response.ErrorListener errorListener, Map<String, String> params) {
super(method, url, listener, errorListener);
this.params = params;
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
return params;
}
}
/**
* string 請求
* @param context 相關上下文
* @param url 網路訪問url
* @param params 網路請求引數
* @param callback 網路請求回撥
*/
public void stringRequest(Context context, String url, Map<String, String> params, OnNetCallback<String> callback){
makeRequest(context, StringRequestImpl.class, url, params, callback);
}
/**
* jsonObject 請求
* @param context 相關上下文
* @param url 網路訪問url
* @param params 網路請求引數
* @param callback 網路請求回撥
*/
public void jsonObjectRequest(Context context, String url, Map<String, String> params, OnNetCallback<JSONObject> callback){
makeRequest(context, JsonObjectRequestImpl.class, url, params, callback);
}
/**
* jsonArray 請求
* @param context 相關上下文
* @param url 網路訪問url
* @param params 網路請求引數
* @param callback 網路請求回撥
*/
public void jsonArrayRequest(Context context, String url, Map<String, String> params, OnNetCallback<JSONArray> callback){
makeRequest(context, JsonArrayRequestImpl.class, url, params, callback);
}
}
ImageLoader.class類
public class ImageLoader {
/** 最大的圖片快取大小 */
private final int MAXDISKCACHEBYTES = 10 * 1024 *1024;
private static volatile ImageLoader instance;
private com.android.volley.toolbox.ImageLoader imageLoader;
private ImageLoader(){
RequestQueue requestQueue = Volley.newRequestQueue(RootApplication.getInstance(), MAXDISKCACHEBYTES);
VolleyLruCache lruCache = new VolleyLruCache();
imageLoader = new com.android.volley.toolbox.ImageLoader(requestQueue, lruCache);
}
public static ImageLoader getInstance(){
if (instance == null){
synchronized (ImageLoader.class){
if (instance == null)
instance = new ImageLoader();
}
}
return instance;
}
/** 通過反射獲取imageview的大小 */
private int getImageViewFieldValue(Object object, String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = (Integer) field.get(object);
if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
value = fieldValue;
}
} catch (Exception e) {
L.e(e);
}
return value;
}
/**
* 載入圖片
* @param url 圖片url
* @param imageView 需要載入圖片的檢視
*/
public void loadImage(String url, final ImageView imageView){
loadImage(url, imageView, null);
}
/**
* 只帶回撥的圖片載入
* @param url 圖片url
* @param listener 圖片載入回撥
*/
public void loadImage(String url, final OnLoadCallBack listener){
loadImage(url, 0, 0, listener);
}
/**
* 帶回撥的載入圖片
* @param url 圖片url
* @param width 需要載入的圖片寬
* @param height 需要載入的圖片高
* @param listener 載入圖片完成回撥
*/
public void loadImage(String url, int width, int height, final OnLoadCallBack listener){
loadImage(url, null, width, height, listener);
}
/**
* 帶回撥的載入圖片
* @param url 圖片url
* @param imageView 需要載入圖片的檢視
* @param listener 載入圖片的回撥
*/
public void loadImage(String url, final ImageView imageView, final OnLoadCallBack listener){
int width = getImageViewFieldValue(imageView, "mMaxWidth");
int height = getImageViewFieldValue(imageView, "mMaxHeight");
loadImage(url, imageView, width, height, listener);
}
/**
* 載入圖片
* @param url 圖片url
* @param imageView 需要載入圖片的檢視
* @param width 需要載入檢視的寬
* @param height 需要載入檢視的高
* @param listener 載入圖片回撥
*/
public void loadImage(String url, final ImageView imageView, int width, int height, final OnLoadCallBack listener){
imageLoader.get(url, new com.android.volley.toolbox.ImageLoader.ImageListener() {
@Override
public void onResponse(com.android.volley.toolbox.ImageLoader.ImageContainer response, boolean isImmediate) {
if (imageView != null)
imageView.setImageBitmap(response.getBitmap());
if (listener != null)
listener.onLoadSuccess(response.getBitmap(), response.getRequestUrl());
}
@Override
public void onErrorResponse(VolleyError error) {
if (listener != null) {
NetError netError = new NetError();
netError.transferVolleyError(error);
listener.onLoadFail(netError);
}
}
}, width, height);
}
/**
* 載入圖片
* @param url 圖片url
* @param imageView 需要載入該圖片的url
* @param defaultImageResId 載入圖片時的預設資源id
* @param errorImageResId 載入圖片失敗時顯示的圖片資源id
*/
public void loadImage(String url, final ImageView imageView, int defaultImageResId, int errorImageResId){
int width = getImageViewFieldValue(imageView, "mMaxWidth");
int height = getImageViewFieldValue(imageView, "mMaxHeight");
loadImage(url, imageView, defaultImageResId, errorImageResId, width, height);
}
/**
* 載入圖片
* @param url 圖片url
* @param imageView 需要載入該圖片的url
* @param defaultImageResId 載入圖片時的預設資源id
* @param errorImageResId 載入圖片失敗時顯示的圖片資源id
* @param width 載入圖片的寬度
* @param height 載入圖片的高度
*/
public void loadImage(String url, final ImageView imageView, int defaultImageResId, int errorImageResId,
int width, int height){
com.android.volley.toolbox.ImageLoader.ImageListener listener =
com.android.volley.toolbox.ImageLoader.getImageListener(imageView,
defaultImageResId, errorImageResId);
imageLoader.get(url, listener, width, height);
}
/**
* 載入圖片回撥
*/
public interface OnLoadCallBack {
void onLoadSuccess(Bitmap bitmap, String url);
void onLoadFail(NetError error);
}
}
上面的兩個類,就是最基本的網路訪問和圖片載入
基於此,還封裝了基本的錯誤類,主要是用來將Volley的exception轉換成通用的exception
NetError.class類
public class NetError extends Exception{
public int errorCode;
public String errorMessage;
/**
* 將volley的錯誤資訊轉換成通用的資訊
*/
public void transferVolleyError(VolleyError error){
if (error.networkResponse != null)
this.errorCode = error.networkResponse.statusCode;
this.errorMessage = error.toString();
}
}
上面就是列舉的第二種封裝方式,封裝了最基本的功能。
以上就是我最基本的想法,寫在這裡來拋磚引玉,不知道這種思想是否正確
原始碼在我的github中,點我
希望大家多多評論
相關文章
- Android Volley框架原始碼解析Android框架原始碼
- 微信支付iOS整合與二次封裝iOS封裝
- Volley 知識梳理 Volley解析
- Flutter Dio二次封裝Flutter封裝
- axios二次封裝iOS封裝
- 二次封裝WebDriverWait封裝WebAI
- axios的二次封裝iOS封裝
- elment dialog二次封裝封裝
- OpenResty Redis操作二次封裝RESTRedis封裝
- FMDB 二次封裝,面向模型封裝模型
- Android框架之Volley與GlideAndroid框架IDE
- Android 開源專案原始碼解析 -->Volley 原始碼解析(十五)Android原始碼
- 封裝Volley使Volley的每個請求都自動儲存和傳送Cookie封裝Cookie
- Dapper的封裝、二次封裝、官方擴充套件包封裝,以及ADO.NET原生封裝APP封裝套件
- axios二次封裝學習iOS封裝
- 二次封裝 query ajax 辦法封裝
- uview-ui toast 二次封裝ViewUIAST封裝
- axios的二次封裝與async,await的配合使用?iOS封裝AI
- Android Volley 原始碼解析(二),探究快取機制Android原始碼快取
- Volley的原理解析
- 社會化登入分享-Android SDK的二次封裝和使用Android封裝
- Selenium二次封裝-Python版本封裝Python
- 對AlamofireObjectMapper進行二次封裝ObjectAPP封裝
- 對Volley框架的一些介面進行封裝:VolleyAir框架封裝AI
- Android網路程式設計(四)從原始碼解析VolleyAndroid程式設計原始碼
- Android Volley 基本使用Android
- selenium-webdriver的二次封裝(十)Web封裝
- 基於Glide4.7.1二次封裝IDE封裝
- 小程式 二次封裝wx.request方法封裝
- notification 全解和 api 的二次封裝API封裝
- FMDB 二次封裝工具類,讓你快速學會封裝,整合資料庫封裝資料庫
- Android Volley 原始碼解析(三),圖片載入的實現Android原始碼
- Android 二次最佳化個人封裝新聞可滑動標題欄Android封裝
- 基於 uber 開源的 zap 二次封裝封裝
- Disruptor 高效能併發框架二次封裝框架封裝
- vue介面請求方式axios二次封裝VueiOS封裝
- Vue二次封裝axios為外掛使用Vue封裝iOS
- 遲到的Volley原始碼解析原始碼