Volley——网络请求框架的使用

时间:2022-12-29 13:32:25

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));
       
}
    }
}