Volley官方地址
http://*.com/ refreshNeeded
https://developer.android.com/training/volley/index.html
Volley概述
Volley是Google 2013年I/O大会推出的针对Android的HTTP网络请求框架,让网络请求更简单,更快。
特点:
l 自动调度网络请求
l 支持并发网络连接
l 支持标准的HTTP缓存协议(由服务器来决定是否缓存数据)
l 支持请求优先级设置(4级)
l 支持取消单个或多个请求
l 易于定制,扩展性强。比如Retry&Backoff机制
l 强大的网络请求能力让你轻松的发送异步请求来填充UI数据
l 提供调试和跟踪工具
优点:
擅长将RPC(远程过程调用协议,C/S模式)类型的操作,用来展示服务器数据。比如以某种数据格式获取一页搜索结果。支持任意的数据传输格式,比如图片,字符串,json,你也可以定义自己的数据格式。其实就是自定义Request。Volley让你不再写这些重复的模板代码(网络请求逻辑,不再重复造*),这样你就可以专注于你应用本身的业务逻辑.
缺点:
由于Volley都是在内存中解析和处理数据,所以不适合大数据量的下载操作。如果需要下载大文件,可以考虑使用系统的DownloadManager。
Volley如何使用
Volley存在于AOSP的frameworks/volley 里。包含了主要的网络请求核心框架,toolbox包下是一些帮你实现的基于核心框架封装的网络请求工具。
1. 直接把代码clone下来,把volley的代码直接copy到你的project里,
git clone https://android.googlesource.com/platform/frameworks/volley
2. 作为一个library项目存在或者jar包存在
Sending a Simple Request(发送一个简单的请求)
首先你需要通过Volley创建一个RequestQueue
,用来添加Request 对象。RequestQueue
管理着网络请求的work thread,执行读写缓存,解析响应结果这些操作。Request负责原始响应的解析,另外还要将解析后的响应抛回给主线程处理。
这一小节的学习内容为,如何通过Volley.newRequestQueue 给我们创建的RequestQueue
发送一个Request请求。同样也会介绍如何把一个Request添加到队列,如何取消一个请求。
1. Add the INTERNET Permission(添加网络权限)
在清单文件中添加权限:
<uses-permissionandroid:name="android.permission.INTERNET"/> |
2. Use newRequestQueue(使用RequestQueue发Request)
使用Volley.newRequestQueue
初始化一个RequestQueue,并发送一个StringRequest。使用Volley执行网络请求,开发者不需要关心线程的问题,只需要在响应的Listener里处理结果,直接更新UI就可以了。
final TextView mTextView = (TextView) findViewById(R.id.text); ...
// Instantiate the RequestQueue. RequestQueue queue = Volley.newRequestQueue(this); String url ="http://www.google.com";
// Request a string response from the provided URL. StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String response) { // Display the first 500 characters of the response string. mTextView.setText("Response is: "+ response.substring(0,500)); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { mTextView.setText("That didn't work!"); } }); // Add the request to the RequestQueue. queue.add(stringRequest); |
步骤:
1.初始化一个RequestQueue
2.构建一个Request,这个Demo里我们使用的是StringRequest
3.将Request对象添加到RequestQueue中
/** * 使用Volley发送一个Request请求 */ public class StringRequestActivity extends AppCompatActivity implements View.OnClickListener {
/** 显示网络响应的TextView */ private TextView mTvContent;
/** 要访问的URL地址*/ private static final String URL = Constants.SERVER + "volleyTest.txt";
/** Volley的请求队列*/ private RequestQueue mRequestQueue;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_string_request); mTvContent = (TextView) findViewById(R.id.tv_content); findViewById(R.id.btn_request).setOnClickListener(this);
// 1. 初始化RequestQueue mRequestQueue = Volley.newRequestQueue(this); }
@Override public void onClick(View v) { // 2. 构建Request,这里因为想获取的是字符串,因此我们构建一个StringRequest StringRequest request = new StringRequest(URL, new Response.Listener<String>() { @Override public void onResponse(String response) { // 请求成功 mTvContent.setText("请求成功:" + response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // 请求失败 mTvContent.setText("请求失败:" + error.getMessage()); } });
// 3. 将Request添加到RequestQueue mRequestQueue.add(request); } } |
3. Send a Request(Volley网络请求交互流程)
当调用add()方法的时候,Volley会运行一个缓存处理线程和一池子网络分发线程。
当添加Request到RequestQueue的时候,请求先被缓存线程处理和鉴别:如果请求在缓存中有,则将缓存的response响应在缓存线程中进行解析并将解析后的响应分发给主线程。
如果缓存中没有这个Request请求,则将这个Request放到网络队列中。第一个可用的网络线程将Request请求从队列中取出来,执行HTTP传输,并在工作线程中解析响应,将响应写进缓存,并将解析好的响应内容传给主线程进行分发。
Volley将像IO、解析等耗时操作都放到了工作线程(子线程)来执行。你可以在任何线程去添加请求,Volley会将响应直接在主线程去进行分发。
4. Cancel a Request(取消请求)
调用Request.cancel()方法可以取消一个请求。
一旦请求被取消,Volley会确保你的响应操作不会被执行。这意味着在实际操作中你可以在activity的 onStop() 方法中取消所有等待在队列中的请求。你不需要通过检测getActivity() == null 这样的判断来略过处理结果,其他类似onSaveInstanceState() 等保护性的方法里面也都不需要检测。
为了利用这种优势,你应该跟踪所有已经发送的请求,以便在需要的时候,可以取消他们。有一个简便的方法 :你可以为每一个请求对象都绑定一个tag对象。你可以使用这个tag来提供取消的范围。
例如,你可以为你的所有请求都绑定到执行的Activity上(以Activity的this作为Request的tag),然后你可以在onStop() 方法执行 requestQueue.cancelAll(this) 来取消这些请求。同样的,你可以为ViewPager中的所有请求图片的Request对象分别打上对应Tab的tag。并在滑动时取消这些请求,用来确保新生成的tab不会被前面tab的请求任务阻塞。
示例:
1. Define yourtag and add it to your requests.
public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue; // Assume this exists.
// Set the tag on the request.
stringRequest.setTag(TAG);
// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
2. In youractivity's onStop() method,cancel all requests that have this tag.
@Override
protected void onStop () {
super.onStop();
if (mRequestQueue != null) {
mRequestQueue.cancelAll(TAG);
}
}
取消Request要谨慎,如果你的程序执行逻辑依赖这次请求的结果,那么就要慎重了。
重申一次,取消以后,响应处理不会被调用。
Setting Up a RequestQueue(配置请求队列)
Set Up a Network and Cache (配置网络和缓存)
一个RequestQueue需要2个东西才能完成它的工作:
1.一个Network来完成Request请求的通信传输
2.一个执行缓存操作的Cache
Volley的toolbox中提供了Network和Cache的标准实现:
1.DiskBasedCache
提供了文件和响应一对一的缓存
2.BasicNetwork
提供了基于你偏向的HTTP Client的网络传输
BasicNetwork
是Volley默认的网络实现,一个BasicNetwork
必须先使用你的应用联网所使用的HTTP Client进行初始化。代表性的HTTP Client就是HttpURLConnection。
RequestQueue源码解析
我们是通过“Volley.newRequestQueue”方法来获取RequestQueue的实例的,我们来看一下newRequestQueue方法的实现:
/** * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. * * @param context A {@link Context} to use for creating the cache dir. * @param stack An {@link HttpStack} to use for the network, or null for default. * @return A started {@link RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context, HttpStack stack) { // 指定缓存文件夹,这里默认采用data/data/包名/cache/volley作为缓存路径
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } // 根据当前SDK版本选择Network,2.3以下使用HttpClient,2.3以上选用HttpURLConnection if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } }
// 构建BasicNetWork Network network = new BasicNetwork(stack);
// 通过DiskBasedCache和BasicNetwork构建RequestQueue实例 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); // 开启消息队列
queue.start();
return queue; }
|
从上面的代码可知,Volley先构建了BasicNetwork和DiskBasedCache,然后将这两个对象作为参数传递给了RequestQueue,构建出了RequestQueue对象。紧接着,调用了RequestQueue.start()方法,开启了消息队列。
我们来看下这个“RequestQueue.start()”中做了什么:
/** * Starts the dispatchers in this queue. */ public void start() {
//开启之前先停止所有的操作 stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it.
//构建缓存线程,并开启缓存线程 mCacheDispatcher =new CacheDispatcher(mCacheQueue,mNetworkQueue,mCache, mDelivery); mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
//构建网络线程,默认构建了4个网络线程,并依次开启这4个网络线程 for(int i = 0;i < mDispatchers.length;i++) { NetworkDispatcher networkDispatcher = newNetworkDispatcher(mNetworkQueue,mNetwork, mCache,mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
|
在“RequestQueue.start()”中,我们先在start之前进行了stop操作,然后先后构建了缓存线程CacheDispatcher和网络线程NetworkDispatcher,并将它们开启了。
说CacheDispatcher和NetworkDispatcher是线程,是因为它们实际上都是Thread的子类:
public classCacheDispatcher extendsThread{…} |
public classNetworkDispatcher extendsThread{…} |
因此,调用CacheDispatcher或者NetworkDispatcher对象的start()方法,最终关键逻辑一定是走的是run()方法。
CacheDispatcher源码解析
我们先看一下CacheDispatcher的run方法做了什么:
@Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache. mCache.initialize(); // 开启了一个死循环,不停地在缓存队列中去Request while (true) { try { // Get a request from the cache triage queue, blocking until // at least one is available.
//从缓存队列中取到Request消息 final Request<?> request =mCacheQueue.take(); request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
//如果这个Request被取消了,则不做任何操作 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; }
// Attempt to retrieve this item from cache.
//根据Request获取缓存的数据实体对象 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) {
request.addMarker("cache-miss"); // Cache miss; send off to the network dispatcher.
//如果没有这个Request的缓存,则将这个Request加入到网络队列中 mNetworkQueue.put(request); continue; }
// If it is completely expired, just send it to the network.
//如果能取到Request的缓存,但是缓存已经过期了,也将Request加入到网络队列去 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; }
// We have a cache hit; parse its data for delivery back to the request. request.addMarker("cache-hit");
//能获取到缓存,将缓存中存储的网络响应的原始数据(字节数组)转换成对应的响应数据 Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) { // Completely unexpired cache hit. Just deliver the response.
//将Request的响应数据Response分发到主线程 mDelivery.postResponse(request,response); } else{ // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry);
// Mark the response as intermediate. response.intermediate= true;
// Post the intermediate response back to the user and have // the delivery then forward the request along to the network. mDelivery.postResponse(request,response, newRunnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch(InterruptedException e) { // Not much we can do about this. } } }); }
} catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }
|
由代码可知:
1.CacheDispatcher被开启的时候,内部实际上是维护这一个while(true)死循环,不停的从队列中取Request;
2. 取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理;
3. 若Request没有被取消,则看是否有这个Request对应的缓存数据,若没有缓存数据,则将Request添加到网络队列中去;
4. 有缓存数据,则判断缓存数据是否已经过期,若数据过期,则添加Request到网络队列;
5. 若缓存数据没有过期,则将缓存的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String;
6. 将解析好的Response响应分发到主线程
将解析好的Response分发到主线程调用的是“mDelivery.postResponse(request, response);”
我们来看一下这个postResponse方法做了什么:
@Override public void postResponse(Request<?> request,Response<?> response,Runnable runnable) { request.markDelivered(); request.addMarker("post-response"); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); } |
而这个mResponsePoster实际上是一个线程池,它重写了自己的execute方法,在execute方法执行时,将对应的Runnable对象借助Handler分发到了主线程去执行:
public classExecutorDelivery implementsResponseDelivery { /** Used for posting responses, typically to the main thread. */ private final ExecutormResponsePoster;
/** * Creates a new response delivery interface. * @param handler {@link Handler} to post responses on */ public ExecutorDelivery(finalHandler handler) { // Make an Executor that just wraps the handler. mResponsePoster =new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; }
……
}
|
因此,我们的Response响应最终会在主线程被调用方接收并进行处理。
NetworkDispatcher源码解析
我们再来看一下NetworkDispatcher的run方法做了什么:
@Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//开启死循环,不停的去队列中取Request while (true) { long startTimeMs = SystemClock.elapsedRealtime(); Request<?> request; try { // Take a request from the queue.
//从缓存队列中取到Request消息 request = mQueue.take(); } catch(InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; }
try { request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the // network request.
//如果这个Request被取消了,则不做任何操作 if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; }
addTrafficStatsTag(request);
// Perform the network request.
//传送请求,获取响应 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already, // we're done -- don't deliver a second identical response. if (networkResponse.notModified&& request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; }
// Parse the response here on the worker thread.
//将网络响应的原始数据(字节数组)转换成对应的响应数据 Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete");
// Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s.
//缓存响应数据 if (request.shouldCache() && response.cacheEntry!= null) { mCache.put(request.getCacheKey(),response.cacheEntry); request.addMarker("network-cache-written"); }
// Post the response back. request.markDelivered();
//将响应的消息分发到主线程 mDelivery.postResponse(request,response); } catch(VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request,volleyError); } catch(Exception e) { VolleyLog.e(e, "Unhandled exception %s",e.toString()); VolleyError volleyError = newVolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request,volleyError); } } }
|
由代码可知:
1.NetworkDispatcher被开启的时候,内部实际上是维护这一个while(true)死循环,不停的从队列中取Request;
2. 取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理;
3. 若Request没有被取消,则根据Request去执行网络请求,获取响应Response;
4. 将响应的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String;
5. 缓存响应的数据Response;
6. 将响应的信息分发到主线程
执行网络网络请求,获取Request对应的响应Response,调用的是“mNetwork.performRequest(request)”方法,实际上底层执行的是HurlStack或者HttpClientStack中的performRequest方法。若是执行HurlStack的performRequest方法,则底层其实是调用的HttpURLConnection;若是执行HttpClientStack的performRequest方法,则底层其实是调用的HttpConnection。
分发Response到主线程的逻辑与CacheDispatcher中的分发逻辑完全一致。
Use a Singleton Pattern(使用单例来管理RequestQueue)
创建一个RequestQueue是非常消耗资源的,在一个Application中只需维护一个RequestQueue的实例就可以了。
在写应用的时候,我们可以采用单例模式来管理RequestQueue的实例,节省资源的开销。
Making a Standard Request(发起一个标准的Volley网络请求)
标准的Volley请求有:
StringRequest、ImageRequest、JsonObjectRequest (JsonArrayRequest)
发起一个ImageRequest
/** * 使用ImageRequest加载网络图片 */ public class ImageRequestActivityextends AppCompatActivityimplements View.OnClickListener {
private ImageViewmIv;
/** 要访问的URL地址*/ private static final StringURL = Constants.SERVER+ "volley.png";
/** Volley的请求队列*/ private RequestQueuemRequestQueue;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_request); mIv = (ImageView) findViewById(R.id.iv); findViewById(R.id.btn_load).setOnClickListener(this);
// 1. 初始化RequestQueue mRequestQueue = Volley.newRequestQueue(this); }
@Override public void onClick(View v) { // 2. 构建ImageRequest (参数3 参数4是压缩比,若不想压缩,直接传0) ImageRequest request = newImageRequest(URL, newResponse.Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { // 请求成功,设置加载到的图片 mIv.setImageBitmap(response); } }, 0,0, null, Bitmap.Config.RGB_565, newResponse.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // 请求失败,加载一张默认图片 mIv.setImageResource(R.mipmap.ic_launcher); Toast.makeText(ImageRequestActivity.this,"onErrorResponse===" + error.getMessage(), Toast.LENGTH_SHORT).show(); } });
// 3. 将Request添加到RequestQueue mRequestQueue.add(request); } } |
发起一个JsonObjectRequest
/** * 发送JsonObjectRequest请求Json数据 */ public class JsonObjectRequestActivityextends AppCompatActivityimplements View.OnClickListener {
/** 显示网络响应的TextView */ private TextViewmTvContent;
/** 要访问的URL地址*/ private static final StringURL = Constants.SERVER+ "topic";
/** Volley的请求队列*/ private RequestQueuemRequestQueue;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_json_object_request); mTvContent = (TextView) findViewById(R.id.tv_content); findViewById(R.id.btn_request).setOnClickListener(this);
// 1. 初始化RequestQueue mRequestQueue = Volley.newRequestQueue(this); }
@Override public void onClick(View v) { try { // 构建向服务器发送的参数 JSONObject params = newJSONObject(); params.put("page","1"); params.put("pageNum","8");
// 2. 构建JsonObjectRequest(参数2是向服务器传递的参数) JsonObjectRequest request = newJsonObjectRequest(URL,params,
new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { // 请求成功 mTvContent.setText("请求成功:"+ response.toString()); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // 请求失败 mTvContent.setText("请求失败:"+ error.getMessage()); } });
// 3. 将Request添加到RequestQueue mRequestQueue.add(request); } catch(JSONException e) { e.printStackTrace(); } } }
|
通过ImageLoader加载图片
ImageLoader是一个帮助类,用于加载和缓存网络图片。
ImageLoader适用于大量的ImageRequest请求,比如在ListView中加载大量的缩略图。ImageLoader提供了内存缓存,这个内存缓存在Volley的缓存之前被使用,来防止图片闪烁。
ImageLoader不会阻塞主线程,当执行IO操作的时候,这一点尤为重要。
ImageLoader可以合并网络响应,使它可以同时分发多个网络响应,极大地改善了性能。
/** * 使用ImageLoader加载网络图片 */ public class ImageLoaderActivityextends AppCompatActivityimplements View.OnClickListener {
private ImageViewmIv;
/** 要访问的URL地址*/ private static final StringURL = Constants.SERVER+ "volley.png";
/** Volley的请求队列*/ private RequestQueuemRequestQueue;
/** ImageLoader对象*/ private ImageLoadermImageLoader;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_loader); mIv = (ImageView) findViewById(R.id.iv); findViewById(R.id.btn_load).setOnClickListener(this);
// 1. 初始化RequestQueue mRequestQueue = Volley.newRequestQueue(this);
// 2. 初始化ImageLoader,不使用内存缓存,则给一个默认的ImageCache,不重写里面的方法 mImageLoader =new ImageLoader(mRequestQueue, newImageLoader.ImageCache() { @Override public BitmapgetBitmap(String url) { return null; }
@Override public void putBitmap(String url,Bitmap bitmap) {
} }); }
@Override public void onClick(View v) { // 3. 使用ImageLoader加载图片 mImageLoader.get(URL, ImageLoader.getImageListener(mIv, R.mipmap.ic_launcher, R.mipmap.ic_launcher)); } } |
NetworkImageView
NetworkImageView是基于ImageLoader的,它可以替代ImageView去加载网络图片。
在布局文件中使用NetworkImageView:
<com.android.volley.toolbox.NetworkImageView android:id="@+id/niv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true"/> |
在Java代码中获取NetworkImageView的实例,并调用其setImageUrl方法:
/** * 使用NetworkImageView加载网络图片 */ public class NetworkImageViewActivity extends AppCompatActivity implements View.OnClickListener {
private NetworkImageView mNiv;
/** 要访问的URL地址*/ private static final String URL = Constants.SERVER + "volley.png";
/** Volley的请求队列*/ private RequestQueue mRequestQueue;
/** ImageLoader对象*/ private ImageLoader mImageLoader;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_network_image_view); mNiv = (NetworkImageView) findViewById(R.id.niv); findViewById(R.id.btn_load).setOnClickListener(this);
// 1. 初始化RequestQueue mRequestQueue = Volley.newRequestQueue(this);
// 2. 初始化ImageLoader,不使用内存缓存,则给一个默认的ImageCache,不重写里面的方法 mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() { @Override public Bitmap getBitmap(String url) { return null; }
@Override public void putBitmap(String url, Bitmap bitmap) {
} }); }
@Override public void onClick(View v) { // 使用NetworkImageView加载网络图片 mNiv.setImageUrl(URL, mImageLoader); } } |
Implementing a Custom Request(自定义Request)
自定义Request需要实现两步:
1.继承Request<T>,其中T表示转换成的Response的类型
2.实现抽象方法 parseNetworkResponse()
和 deliverResponse()
parseNetworkResponse:
解析相应数据并进行封装,然后将响应分发出去。
换句话说就是,将响应返回的原始的字节数组解析成想要的数据类型,然后将解析好的数据分发出去。
@Override protected Response<T> parseNetworkResponse( NetworkResponse response) { try { String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response)); } // handle errors ... } |
deliverResponse:
Volley将你从parseNetworkResponse中返回的对象分发到主线程。
protected void deliverResponse(T response) { listener.onResponse(response); } |
范例:自定义GsonRequest
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson(); private final Class<T> clazz; private final Map<String, String> headers; private final Response.Listener<T> listener;
/** * Make a GET request and return a parsed object from JSON. * * @param url URL of the request to make * @param clazz Relevant class object, for Gson's reflection * @param headers Map of request headers */ public GsonRequest(String url, Class<T> clazz, Map<String, String> headers, Response.Listener<T> listener, Response.ErrorListener errorListener) { super(Method.GET, url, errorListener); this.clazz = clazz; this.headers = headers; this.listener = listener; }
@Override public Map<String, String> getHeaders() throws AuthFailureError { return headers != null ? headers : super.getHeaders(); }
@Override protected void deliverResponse(T response) { listener.onResponse(response); }
@Override protected Response<T> parseNetworkResponse(NetworkResponse response) { try { String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JsonSyntaxException e) { return Response.error(new ParseError(e)); } } } |