这一节我们介绍自定义Request。
在上一节中我们只介绍了StringRequest
的POST的请求方式,而没有介绍JsonObjectRequest
和JsonArrayReqest
的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
实现的,当然ImageLoader
比ImageRequest
更高一些,它不仅仅对图片进行了缓存还过滤了重复的链接,避免重复请求;当然使用也稍微麻烦一些。
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);
上面的方法的第一个参数,是要显示图片的控件;第二个参数是默认图;第三个图片是下载失败要显示的图片。
最后我们只需要在调用 ImageLoader
的 get()
方法就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里除了使用 ImageRequest
和Imageloader
加载网络图片,还提供了第三种方式,那就是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_width
和android:layout_height
属性就可以了,它会在内部自动化完成。