Volley 的介绍和使用(三)

这一节我们介绍自定义Request。
在上一节中我们只介绍了StringRequest的POST的请求方式,而没有介绍JsonObjectRequestJsonArrayReqest的POST请求方式,因为这两个扩展至Request的类,覆盖了getBody()方法,把Request的encode 请求参数的方法修改了,把构造函数中的JSONObject对象转换成String, 直接发送出去了;所以我们要实现POST请求,需要自己实现一个Request

自定义Request

自己自定义一个Request,需要继承Request类,类似于StringRequest中的实现。主要重写一下parseNetworkResponse()方法,这个方法用来解析网络请求的结果。以下是 StringRequst 的源码:

public class StringRequest extends Request<String> {
    //用于传递请求结果的监听器
    private final Listener<String> mListener;
    //根据给出的请求方式,创建一个新的请求
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    //创建一个GET 请求    
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.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));
    }
}

类似我们创建一个JsonPostRequest:

public class JsonPostRequest extends Request<JSONObject> {
    //用于传递请求结果的监听器
    public Response.Listener<JSONObject> mListener;
    private Map<String, String> mParames;
    //根据给出的请求方式,创建一个新的请求
    private JsonPostRequest(@PostOnly int method, String url, Map<String, String> parames, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mParames = parames;
        this.mListener = listener;
    }
    //创建一个POST请求
    public JsonPostRequest(String url, Map<String, String> parames, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
        this(POST, url, parames, listener, errorListener);
    }
    // 获取请求参数
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return mParames;
    }

    /**
     * //这个方法主要是将请求返回的数据进行解析等处理
     * @param response Response from the network
     * @return Response 对象,包含解析过的数据
     */
    @Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            //将数据封装成JSONObject
            return Response.success(new JSONObject(parsed), HttpHeaderParser.parseCacheHeaders(response));
        } catch (JSONException e) {
            return Response.error(new ParseError(e));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }
    @Override
    protected void deliverResponse(JSONObject response) {
        mListener.onResponse(response);//传递请求结果
    }

    public static final int POST = Method.POST;

    /**
     * 创建一个注解,用来限制参数类型,这里只能传入POST这个参数
     */
    @IntDef({POST})
    public @interface PostOnly {
    }
}

这样我们就可以得到一个请求返回是JSONObject的请求类了。我们可以这样使用它。

private void requestByVolley(){
    //1.组拼URL
    String mUrlStr = "http://op.juhe.cn/onebox/news/query";
    //2.获取一个RequestQueue
    RequestQueue queue = App.getQueue();
    //3.设置POST请求的参数
    Map<String, String> parames = new HashMap<>();
    parames.put("q", "普京");//搜索关于普京的新闻
    parames.put("key", APP_KEY);
    //4.创建一个请求,其返回的结果以String 形式返回
    JsonPostRequest requst = new JsonPostRequest(mUrlStr, parames,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    mTextView.setText(response.toString());
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    mTextView.setText(error.getMessage());
                }
            });
    //5.将请求添加的请求队列中,在这里请求就已经开始了
    queue.add(requst);
}

使用方式类似于之前的 JsonObjectRequst 的GET请求方式;类似的我么也可以做其它形式的扩展,例如可以返回 JSONArray ,可以返回某个需要的对象,当然也可以把 Gson 嵌入进去。只需要 修改一下 parseNetworkResponse()就OK,在这里做你想要的操作。

ImageLoader

上一节介绍了 ImageRequest ,可以很简单的实现对网络图片的请求。但是这种方式是没有的缓存的,如果需要缓存就需要 ImageLoader ;它的内部实现其实也是使用ImageRequest实现的,当然ImageLoaderImageRequest更高一些,它不仅仅对图片进行了缓存还过滤了重复的链接,避免重复请求;当然使用也稍微麻烦一些。

ImageLoader 的构造方法中,需要一个RequestQueue 和一个ImageCache

public ImageLoader(RequestQueue queue, ImageCache imageCache) {
    mRequestQueue = queue;
    mCache = imageCache;
}

所以我们需要一个 RequestQueue 和一个ImageCache 。当然如果我们还需要一个监听器,用来监听下载图片是否成功,如果成功就显示下载的图片,如果失败就显示失败图片,在下载过程中,我们不能让界面空着,也要给它一个默认图片。当然ImageLoader提供了这样的监听器 ImageListener。下面看看它:

ImageLoader.ImageListener listener = ImageLoader.getImageListener(mImageView, R.drawable.default_icon, R.drawable.error_icon);

上面的方法的第一个参数,是要显示图片的控件;第二个参数是默认图;第三个图片是下载失败要显示的图片。

最后我们只需要在调用 ImageLoaderget()方法就OK了。
上面说的比较简单,下面我们上代码。

private void loadImageByImageLoader() {
    //1.图片的URL
    String imageURL = "http://c.hiphotos.baidu.com/image/pic/item/0dd7912397dda1449fad6f63b6b7d0a20df486be.jpg";
    //2.请求队列
    RequestQueue queue = App.getQueue();
    //3.使用请求队列和ImageCache构造一个ImageLoader
    ImageLoader loader = new ImageLoader(queue, new ImageLoader.ImageCache() {
        @Override
        public Bitmap getBitmap(String url) {
            return null;
        }

        @Override
        public void putBitmap(String url, Bitmap bitmap) {
        }
    });
    //4.构造一个图片下载的监听器
    ImageLoader.ImageListener listener = ImageLoader.getImageListener(mImageView, R.drawable.default_icon, R.drawable.error_icon);
    //5.下载图片
    loader.get(imageURL, listener);
}

上面只是一个简单的应用,还没有用到ImageLoader最重要的点,那就是缓存,因为上面的ImageCache是一个空的,完全没有缓存的作用,所以我们还要写一个ImageCache,这就用到了Android提供的LRUCahce,当然也很简单,上代码:

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) // LruCache 这个类是在Android 3.1出现的所以要加注解来标识一下
public class LruBitmapCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }
    //设置最大内存
    public LruBitmapCache(int sizeInMib) {
        super(sizeInMib);
    }
    // 用来计算Bitmap的大小
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
    /**
     * 获取默认的缓存大小,为运行时堆内存的最大内存的 1/8。
     *
     * @return 缓存大小值,以Mib为单位
     */
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024);
        return maxMemory / 8;
    }
}

我们继承及成LruCache并且实现了ImageCache接口,设置了图片的缓存大小。我使用的时候可以下面这样:

 ImageLoader mLoader= new ImageLoader(queue, new LruBitmapCache());

有没有感觉这样太麻烦了,每次都要写这么一大堆,当我们把ImageCache实现之后,可以把相应的代码抽取出来,做一个单例类,每次使用的时候只需要一样代码就可以实现。具体代码如下:

public class ImageLoaderManager {
    private RequestQueue mQueue;
    private ImageLoader mLoader;
    public static ImageLoaderManager getInstance() {
        return ImageLoaderManagerInstance.loaderManager;
    }
    private ImageLoaderManager() {
        //1.请求队列
        mQueue = App.getQueue();
    }
    private static final class ImageLoaderManagerInstance {
        final static ImageLoaderManager loaderManager = new ImageLoaderManager();
    }
    public void load(ImageView view,String imageURL){
        //2.使用请求队列和ImageCache构造一个ImageLoader
        mLoader = getImageLoader();
        //3.构造一个图片下载的监听器
        ImageLoader.ImageListener listener = ImageLoader.getImageListener(view, R.drawable.default_icon, R.drawable.error_icon);
        //4.下载图片
        mLoader.get(imageURL, listener);
    }

    public ImageLoader getImageLoader() {
        if(mLoader == null){
            mLoader= new ImageLoader(mQueue, new LruBitmapCache());
        }
        return mLoader;
    }
}

当我们可以这样使用:

String imageURL = "http://c.hiphotos.baidu.com/image/pic/item/0dd7912397dda1449fad6f63b6b7d0a20df486be.jpg";
ImageLoaderManager.getInstance().load(mImageView,imageURL);

NetworkImageView

在Volley里除了使用 ImageRequestImageloader加载网络图片,还提供了第三种方式,那就是NetworkImageView;它是一个自定义的View,继承自ImageView;它比其它两种方式都好用,具体用法如下:

首先 在布局里面添加NetWorkImageView

 <com.android.volley.toolbox.NetworkImageView
     android:id="@+id/networkImageView"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"/>

然后在Activity里面获取这个控件

mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);

最后给NetworkImageView设置默认图,失败图,和图片的URL;需要注意的是,在设置图片URL的时候需要用到上面的ImageLoader;代码如下:

    mNetworkImageView.setDefaultImageResId(R.drawable.default_icon);
    mNetworkImageView.setErrorImageResId(R.drawable.error_icon);
    mNetworkImageView.setImageUrl(imageURL,ImageLoaderManager.getInstance().getImageLoader());

这样就OK了。

在布局里面,没有给NetworkImageView设置具体的宽高,这样不会对图像进行压缩,如果想压缩就给它设置 android:layout_widthandroid:layout_height属性就可以了,它会在内部自动化完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值