上一篇文章中,咱讨论了使用Volley.newRequestQueue方法充分利用Volley默认的优势创建一个请求队列。那么,接下来我们继续讨论如何创建一个自定义的请求队列。
我们来看一下RequestQueue这个类,它的一个构造方法如下:
/** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests * @param threadPoolSize Number of network dispatcher threads to create * @param delivery A ResponseDelivery interface for posting responses and errors */ public RequestQueue(Cache cache,NetWork network,int threadPoolSize, ResponseDelivery delivery){ mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; }
可以看出请求队列需要两样东西:一个能够执行传送请求的网络和一个能够处理缓存的高速缓存存储器。在Volley的toolbox保中已经有着两样必须类的标准实现:DiskBasedCache类为每次响应一个文件的高速缓存器提供一个内存索引,基于你选择的Android或者HttpURLConnection,BasicNetwork类提供了一个网络传输。
BasicNetwork是Volley默认的网络实现类。这个类必须被HTTP客户端初始化,这样我们的应用程序就能够进行网络连接。一般这个HTTP客户端为AndroidHttpClient或者HttpConnection。API低于9的应用需要使用AndroidHttpClient,在低于API9的不能使用HttpURLConnection。
我们跟进去看一下BasicNetwork类的构造方法:
public BaiscNetwork(HttpStack httpstack){ //If a pool isn't passed in,then build a small default pool that will give us //a lot of benefit and not use much memory this(httpstack,new ByteArrayPool(DEFAULT_POOL_SIZE)); }
为了创建尽可能适合所有版本的应用程序,合理的做法就是先检测一下我们的app运行在怎样的设备下,得到设备系统的版本之后,再选择合适的Http客户端。举个栗子:
HttpStack stack; .... //判断设备的系统是否低于API9 if(Build.VERSION.SDK_INT >= Build.VERSION_CODS.GINGERBREAD){ //stack使用HttpURLConnection }else{ //stack使用AndroidHttpClient } Network network = new BasicNetwork(stack);
根据RequestQueue的构造函数,我们可以知道要想执行创建请求队列操作,我们必须调用RequestQueue类中的start方法,我们来看下源代码:
/** *启动队列的请求调度 */ public void start(){ stop(); //终止所有正在运行的调度 //创建缓存调度并启动缓存调度 mCacheDispatcher = new CacheDispatcher(mCacheQueue,mNetworkQueue,mCache,mDelivery); mCacheDispatcher.start(); //创建网络调度 for(int i = 0; i< mDispatcher.length;i++){ NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue,mNetwork,mCache,mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
接下来我们看一些volley的toolbox都给我们实现了那些类型的请求,可以看到,有ImageRequest、JsonArrayRequest、JsonObjectRequest和StringRequest类型的请求。我们挑StringRequest比较常见的分析一下:这种类型的请求一般是为了检索一个给定字符串类型的URL地址的相应内容。StringRequest类继承了Request类,着重看下它的构造方法:
/** *通过给请的方法(post、get等方法)创建一个新的请求 * *@param method 请求的方法类型 *@param url 获取字符串的url *@param listener 监听接受响应的字符串 *@param errorListener 错误监听器,或者为null,忽略错误 public StringRequest(int method,String url,Listener<String>listener, ErrorListener errorListener){ super(method,url,errorListener); mListener = listener; }
最后在这一小节中看一下设置缓存和网络的完整代码:
RequestQueue mRequestQueue; // 实例化缓存 Cache cache = new DiskBasedCache(getCacheDir(),1024*1024); // 设置network,使用HttpURLConnection作为HTTP客户端。 Network network = new BasicNetwork(new HurlStack()); //使用cache和network实例化请求对了 mRequestQueue = new RequestQueue(cache,network); // 启动请求队列 mRequestQueue.start(); String url ="http://www.myurl.com"; // 制定请求和处理响应 StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String response) { // Do something with the response } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // Handle erro } }); // 把制定的请求添加到请求队列中 mRequestQueue.add(stringRequest); ....
如果请求仅是发出一次或者网络调度(NetworkDispatcher)不撤离线程池,那么可以在需要的任何地方创建请求队列,并在对请求做出响应之后或者是返回错误信息之后调用stop方法。遇到上述的情景,可以通过上一篇文章写的"发送简单请求"来处理。但是,通常都会把请求队列类实现为单例,在app启动的时候运行,直至app运行的整个生命周期。
如果app连接网络比较频繁,最合理的方式就是把RequestQueue实现为单实例类型,并这个实例在app运行的整个生命周期中存活。有两种方式实现单实例,推荐的一种做法就是实现一个封装了请求队列和其他volley功能的单例类。还有一种不建议使用的方式就是创建一个继承Application的字类,并在Application.onCreate()方法中创建请求队列。一个静态的单实例类能够以模块化的方式提供同样的功能。
使用推荐的方式实现单实例最重要的概念就是请求队列对象必须以应用上下文(application context)而不是活动上下文(activity context)的形式进行实例化。以此确保了请求队列对象在app运行的整个生命周期中存在,而不是随着活动的重新创建而创建。结合设计模式单例模式的实现,来看看MySingleton.java类的实现,这个类提供了一个请求队列和图片加载:
private static MySingleton mInstance; private RequestQueue mRequestQueue; private ImageLoader mImageLoader; private static Context mCtx; private MySingleton(Context context){ mCtx = context; mRequestQueue = getRequestQueue(); mImageLoader = new ImageLoader(mRequestQueue,n new ImageLoader.ImageCache(){ private final LruCache<String,Bitmap>(20) cache = new LruCache<String,Bitmap>(20); @Override public Bitmap getBitmap(String url){ return cache.get(url); } @Override public void putBitmap(String url,Bitmap bitmap){ cache.put(url,bitmap); } }); } //异步获取单实例 public static synchronized MySingleton getInstance(Context context){ if(mInstance == null){ mInstance = new MySingleton(context); } return mInstance } public RequestQueue getRuquestQueue(){ if(mRequestQueue == null){ //getApplication()方法返回一个当前进程的全局应用上下文,这就意味着 //它的使用情景为:你需要一个生命周期独立于当前上下文的全局上下文, //即就是它的存活时间绑定在进程中而不是当前某个组建。 mRequestQueue = Volley.newRequestQueue(mCtx.getApplication()); } return mRequestQueue; } public <T> void addToRequestQueue(Request<T>req){ getRequestQueue.add(req); } public ImageLoader getImageLoader(){ return mImageLoader; }
在上面的代码中,构造方法中封装了请求队列和图片加载,接着就是异步获取实例、获取请求队列、把请求添加到请求队列、获取图片加载。
最后我们使用这个单例类执行请求队列操作:
//获取请求队列 RequestQueue queue = MySingleton.getInstance(this.getApplication(). getRequest(); ....... //把请求(在这个例子中请求为StringRequest)添加到请求队列 MySingleton.getInstance(this).addToRequestQueue(StringRequest);