最近看到一本书,其中有一句话写的不错:实际上,将第三方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 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{        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  void makeRequest(final Context context, Class<?> clazz, String url, final Map params, final OnNetCallback 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() {                @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 params;        public StringRequestImpl(int method, String url, Response.Listener listener,                                 Response.ErrorListener errorListener, Map params) {            super(method, url, listener, errorListener);            this.params = params;        }        @Override        protected Map getParams() throws AuthFailureError {            return params;        }    }    /**     * 对{@linkplain JsonObjectRequest}的封装类     */    private static class JsonObjectRequestImpl extends JsonObjectRequest{        private Map params;        public JsonObjectRequestImpl(int method, String url, Response.Listener listener,                                 Response.ErrorListener errorListener, Map params) {            super(method, url, listener, errorListener);            this.params = params;        }        @Override        protected Map getParams() throws AuthFailureError {            return params;        }    }    /**     * 对{@linkplain JsonArrayRequest}的封装类     */    private static class JsonArrayRequestImpl extends JsonArrayRequest{        private Map params;        public JsonArrayRequestImpl(int method, String url, Response.Listener listener,                                 Response.ErrorListener errorListener, Map params) {            super(method, url, listener, errorListener);            this.params = params;        }        @Override        protected Map getParams() throws AuthFailureError {            return params;        }    }    /**     * string 请求     * @param context 相关上下文     * @param url 网络访问url     * @param params 网络请求参数     * @param callback 网络请求回调     */    public void stringRequest(Context context, String url, Map params, OnNetCallback 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 params, OnNetCallback 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 params, OnNetCallback 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中,点我

希望大家多多评论



更多相关文章

  1. 在Android图片缓存
  2. ListView分页加载数据
  3. android 获取网络图片缓存(内存—>文件—>网络)
  4. Android加载Bitmap出现OutofMemoryError的原因(官方译文)
  5. android中Bitmap导致的内存溢出
  6. 利用WCF与Android实现图片上传并传参
  7. Android(安卓)加载不同 DPI 资源与内存消耗间的关系
  8. android 微信朋友圈相册封面裁剪的小秘密
  9. Android(安卓)Camera生成bmp格式的图片

随机推荐

  1. android 判断 网络 类型
  2. 如何把android设备中的固件dump出来
  3. 使用NanoHTTPD在android实现web迷你服务
  4. Android 技巧 - 自动生成 Action Bar The
  5. Android PendingIntent和Intent
  6. android获取系统当前年月日时分秒的时间
  7. 修改android文件系统为可读可写
  8. Android中Data和String数据类型转换
  9. Android仿WIN8系统磁贴点击下沉倾斜效果
  10. Android NDK编译时出现的问题以及解决办