使用Volley传输网络数据(官网教程)

介绍:

Volley是一个HTTP库,使Android应用程序变得更加容易,最重要的是,网络速度更快。并且是开源的。


Volley有以下特点:
*自动网络请求调度
*多个并发的网络连接
*透明的磁盘和内存响应缓存与标准HTTP缓存一致性。
*支持请求优先级。
*取消请求API。你可以取消一个请求,或者你可以设定取消请求的块或范围。
*易于定制,例如,重试和补偿。
*订制你异步从网络获取的数据,以便以填充UI
*调试和跟踪工具。




Volley 擅长RPC-type操作,用于填充UI,如抓取页面搜索结果的结构化数据。它轻易与任何协议集成,开箱即用,支持原始字符串,图片,和JSON。你需要通过提供内置支持的特性,Volley 让你专注于你应用程序的逻辑。

*RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。


Volley 不适合大型下载或流媒体业务,因为Volley 在内存中保存所有响应。对于大型下载操作,考虑使用另一种像DownloadManager


Volley库的核心是在开放AOSP库开发框架, 在Volley 的 “toolbox” 包含请求调度主要管道以及一套普遍适用的实用程序”。

AOSP:"Android Open-Source Project"的缩写 ,中文意为"Android 开放源代码项目"


下载


添加Volley 到您的项目的最简单方法是克隆Volley 存储库并将它设置为一个library项目:


通过git :

git clone https://android.googlesource.com/platform/frameworks/volley


下载源代码,导入到你的应用程序项目中作为一个Android library项目(如管理项目从Eclipse ADT所述,如果你使用Eclipse)或使用volley.jar文件。


教程

1.发送一个简单的请求

学习如何发送一个简单的请求使用Volley 的默认行为,以及如何取消请求。

在高版本别上,通过Volley创建一个RequestQueue,并请求对象。RequestQueue管理工作线程上的网络操作,读取或写入缓存,并解析响应。请求做解析原始响应和Volley将解析后的结果返回给主线程

这节课描述了如何使用Volley发送一个请求。Volley.newRequestQueue()方法,建立了RequestQueue给你。在下一个课,建立RequestQueue,如何设置RequestQueue。

这节课还描述了如何添加一个请求到RequestQueue,和取消一个请求。


1.1 添加网络权限

使用Volley,您必须添加 android.permission. INTERNET 在 AndroidManifest.xml中。没有这个,你的程序不能连接到网络。


1.2 使用newRequestQueue

Volley 提供了一种便利方法。为你设置了一个Volley.newRequestQueue(),使用默认值,启动队列。例如:

final TextView mTextView = (TextView) findViewById(R.id.text);
...

// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="https://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);
Volley 总是在主线程传递结果。运行在主线程与接收的数据方便填充UI控件,您可以自由地修改UI控件直接从您的响应处理程序,但它是尤其重要的许多重要的语义提供的库,尤其是取消请求。

1.3 发送一个请求

发送一个请求,只需建立一个RequestQueue,并使用add()将它添加到RequestQueue,如上所示。一旦你添加请求,它穿过管道维修,有其原始响应解析和交付。


当您调用add(),Volley 运行一个缓存处理线程和一个网络调度线程池。当你添加一个请求队列:如果请求存在缓存中,缓存的响应结果将返回给主线程。如果请求不能从缓存获取,它被放在网络队列。第一个可用的网络线程从队列中获得请求,执行HTTP事务,响应工作线程,将响应写入缓存,解析过的响应交付给主线程。


注意,耗时的操作,比如阻塞I / O和解析/解码工作线程上执行。您可以从任何线程添加一个请求,但反应总是在主线程传递。


下图说明了请求的生命周期:


1.4 取消请求

取消请求,调用request.cancel()。一旦取消,Volley 保证你的响应处理程序永远不会被调用。这意味着你可以取消你所有的挂起(pending)的请求在Activity的onStop()方法,你不必检查getActivity()== null 来回收响应处理程序,是否onSaveInstanceState()已经被调用,或其他defensive boilerplate。
利用这种行为,为了能够取消他们在适当的时候,你通常需要跟踪所有动态请求。有一个简单的办法:你为每个请求设置一个tag。然后,您可以使用这个标记提供取消的请求范围。例如,您可以将你的所有请求标记为他们所在的Activity,并在onStop()方法中调用requestQueue.cancelAll(this)。同样,在 ViewPager Tab 中,你可以标记所有缩略图请求与各自的 Tab : 在 Tab 消失时 cancel() 
 
这里是一个例子,它使用一个字符串值的tag:

 

//1.Define your tag 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 your activity's onStop() method, cancel all requests that have this tag.
@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

当取消请求需要小心。如果你根据你的响应处理程序推进状态或启动另一个过程中,你需要考虑这个。再次,响应处理程序将不会被调用。


2.设置一个RequestQueue

了解如何设置RequestQueue,如何实现单例模式创建一个RequestQueue持续你的应用程序的生命周期。


之前的课程 学习了Volley.newRequestQueue 创建RequestQueue。这节课将引导您完成的明确的步骤创建一个RequestQueue,允许您提供自己的自定义行为。
这节课还描述了建议创建一个 单例模式 的 RequestQueue,这使得RequestQueue持续在你的应用程序的生命周期。

2.1 建立一个网络和缓存

RequestQueue需要两件事情来做它的工作:执行请求的传输网络,处理缓存的缓存。Volley的 toolbox中有些可用的 标准实现 : DiskBasedCache 根据一个内存中的索引提供 one-file-per-response缓存,并根据您选择BasicNetwork提供了一个网络传输 Android 的 HttpClient 或 HttpURLConnection。


BasicNetwork 是 Volley 的默认网络的实现。BasicNetwork 必须初始化你应用程序连接网络的HTTP客户机。通常这是 的 HttpClient 或 HttpURLConnection:


针对Android API级别低于9(姜饼)的应用, 使用Android 的 HttpClient 。9(姜饼)之前,HttpURLConnection是不可靠的。更多关于这个话题的讨论中,看Android 的 HTTP Clients 
使用HttpURLConnection应用针对Android API级别9(姜饼)和更高。


创建一个应用程序运行在所有版本的Android系统,您可以检查版本的Android设备,选择适当的HTTP客户端运行,例如:
HttpStack stack;
...
// If the device is running a version >= Gingerbread...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
    // ...use HttpURLConnection for stack.
} else {
    // ...use AndroidHttpClient for stack.
}
Network network = new BasicNetwork(stack);

设置RequestQueue所涉及的步骤:
RequestQueue mRequestQueue;

// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap

// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());

// Instantiate the RequestQueue with the cache and network.
mRequestQueue = new RequestQueue(cache, network);

// Start the queue
mRequestQueue.start();

String url ="https://www.myurl.com";

// Formulate the request and handle the response.
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 error
    }
});

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
...
如果你只需要一次性请求并且不想离开线程池,您可以创建RequestQueue只要你需要,调用 stop()  一旦你的响应或错误回来,使用Volley.newRequestQueue()方法发送一个简单的请求。但更常见的用例是创建RequestQueue作为一个单例,生命周期保持在运行你的应用程序,在下一节中所述。

2.2 使用单例模式

如果应用程序经常使用的网络,这可能是最有效的建立一个单例的RequestQueue 持续在你的应用程序的生命周期。你可以用不同的方法实现这一目标。推荐的方法是实现一个单例类,封装了RequestQueue和其他Volley 功能。另一种方法是创建Application的子类在onCreate()中设置RequestQueue。但是这种方法是不推荐的; 静态单例可以提供相同的功能更模块化的方式。
一个关键概念是RequestQueue必须实例化 Application 的 context ,而不是 Activity 的 context 。这将确保RequestQueue将在你的应用程序的生命周期内,而不是每次都在重新创建Activity重新创建(例如,当用户旋转设备)。


这里是单例类的一个例子,它提供了RequestQueue和ImageLoader功能:
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,
                new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap>
                    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 getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
    getRequestQueue();
...

// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);


3.建立一个标准的请求

学习如何发送一个请求使用一个Volley 的开箱即用的请求类型(原始字符串、图像和JSON)。

本课程介绍如何使用Volley支持的通用请求类型,:


StringRequest:指定一个URL,接收响应的原始字符串。
ImageRequest:指定一个URL,接收一个图像作为回应。
JsonObjectRequest和JsonArrayRequest(JsonRequest 的子类):指定一个URL,返回JSON对象或数组。

如果你的预期响应这些类型之一,你可能不需要实现一个自定义的请求。本课程介绍如何使用这些标准的请求类型。关于如何实现自己的自定义的信息请求,看实现一个自定义的请求。

3.1 请求一个图像


Volley 提供以下类来请求图像。这些类层之上的相互提供不同层次的处理图片:

ImageRequest :给定的URL并且返回一个bitmap。它还提供了方便的特性,如指定调整大小。它的主要好处是, Volley的线程调度确保处理图片的耗时操作 (解码、调整大小) 自动发生在一个工作线程。

ImageLoader : 一个helper类,处理加载并且缓存图像从远程url。ImageLoader是大量ImageRequests的协调器,例如在ListView显示多个缩略图。ImageLoader 提供了一个内存中的缓存,这一点很重要,以防止闪烁。这使得它可以实现缓存,并且不阻止或推迟主线程, 使用磁盘 I/O 时这是不可能的。ImageLoader 也  response coalescing, 几乎每一个响应处理程序将一个位图视图和导致布局通过每个图像。合并可以同时实现多个响应,这有助于提高性能。

NetworkImageView :基于 ImageLoader 有效地取代 ImageView 在通过URL获取网络图像的情况下。NetworkImageView 还可以设置在view从hierarchy中detached时取消挂起的请求。

3.1.1 使用ImageRequest

下面是一个使用ImageRequest的示例。它检索指定的图像URL并在应用程序显示。请注意,这段代码与RequestQueue交互通过一个单例类(请参阅设置RequestQueue更多关于该话题的讨论):
ImageView mImageView;
String url = "https://i.imgur.com/7spzG.png";
mImageView = (ImageView) findViewById(R.id.myImage);
...

// Retrieves an image specified by the URL, displays it in the UI.
ImageRequest request = new ImageRequest(url,
    new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap bitmap) {
            mImageView.setImageBitmap(bitmap);
        }
    }, 0, 0, null,
    new Response.ErrorListener() {
        public void onErrorResponse(VolleyError error) {
            mImageView.setImageResource(R.drawable.image_load_error);
        }
    });
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(request);

3.1.2 使用ImageLoader和NetworkImageView


您可以使用ImageLoader和NetworkImageView一致有效管理多个图像的显示,如在一个 ListView。在你的XML布局文件,在可以使用 ImageView 的地方,您可以使用NetworkImageView,例如:

<com.android.volley.toolbox.NetworkImageView
        android:id="@+id/networkImageView"
        android:layout_width="150dp"
        android:layout_height="170dp"
        android:layout_centerHorizontal="true" />

使用ImageLoader展示图片

ImageLoader mImageLoader;
ImageView mImageView;
// The URL for the image that is being loaded.
private static final String IMAGE_URL =
    "https://developer.android.com/images/training/system-ui.png";
...
mImageView = (ImageView) findViewById(R.id.regularImageView);

// Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,R.drawable.def_image, R.drawable.err_image));
使用NetworkImageView

ImageLoader mImageLoader;
NetworkImageView mNetworkImageView;
private static final String IMAGE_URL = "https://developer.android.com/images/training/system-ui.png";
...

// Get the NetworkImageView that will display the image.
mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);

// Get the ImageLoader through your singleton class.
mImageLoader = MySingleton.getInstance(this).getImageLoader();

// Set the URL of the image that should be loaded into this view, and
// specify the ImageLoader that will be used to make the request.
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);

上面的片段通过一个单例类访问RequestQueue和ImageLoader,如建立RequestQueue 。这种方法可以确保在您的应用程序的生命周期中创建这些类的单个实例。这很重要的原因ImageLoader(处理的助手类加载和缓存图像),内存缓存的主要功能是允许flickerless旋转。使用单例模式允许 bitmap 缓存比activity存在时间长。反之,如果你在一个activity 中创建ImageLoader ImageLoader将重建以及活动每次用户旋转设备。这将引起闪烁。


3.1.4 LRU缓存例子

Volley toolbox 提供了一个标准缓存通过DiskBasedCache缓存实现类。这类缓存文件直接到硬盘在指定的目录中。但使用ImageLoader,您应该提供一个自定义内存LRU bitmap 缓存实现 ImageLoader 接口。您可能希望设置缓存作为一个单例;讨论这个主题的更多信息,请参见设置RequestQueue。

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import com.android.volley.toolbox.ImageLoader.ImageCache;

public class LruBitmapCache extends LruCache<String, Bitmap>
        implements ImageCache {

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }

    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

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

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}
使用:

RequestQueue mRequestQueue; // assume this exists.
ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(LruBitmapCache.getCacheSize()));


3.2 JSON请求


Volley 为JSON请求提供了以下几个类:

JsonArrayRequest: 给定URL,返回JSONArray响应主体。

JsonObjectRequest :给定URL,返回JSONObject响应主体L,比如允许一个可选的JSONObject作为请求的一部分传递的身体。

两类基于共同基类 JsonRequest。你使用它们遵循相同的基本模式使用其他类型的请求。例如,这个代码片段获取JSON提要并显示为文本的界面:

TextView mTxtDisplay;
ImageView mImageView;
mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);
String url = "https://my-json-feed";

JsonObjectRequest jsObjRequest = new JsonObjectRequest
        (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

    @Override
    public void onResponse(JSONObject response) {
        mTxtDisplay.setText("Response: " + response.toString());
    }
}, new Response.ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        // TODO Auto-generated method stub

    }
});

// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);

4.实现一个自定义的请求

了解如何实现一个自定义的请求。
这节课描述了如何实现自己的自定义请求类型。

4.1 编写一个定制的请求

工具箱中的大多数请求有现成的实现;如果响应是一个字符串,图片,或者JSON,你可能不会需要实现一个自定义的请求。
以下情况,你需要实现一个自定义的请求,这是所有您需要做的:

继承 Request<T> 类, <T> 代表响应的结果类型. 如果你的响应是一个字符串,创建您的自定义请求通过继承 Request<String>。
实现 抽象方法 parseNetworkResponse() 和 deliverResponse() ,后面将更详细地描述。


4.1.1 parseNetworkResponse

对于一个给定的类型(如字符串、图像或JSON),响应封装,解析响应交付,。下面是一个示例, 实现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
...
}
请注意以下几点:

NetworkResponse parseNetworkResponse()作为它的参数,其中包含响应负载作为一个byte[],HTTP状态码和响应头。

您的实现必须返回一个Response<T>,其中包含你的输入响应对象和缓存元数据或错误,比如在解析失败的情况下。

如果你的协议标准语义缓存,您可以构建一个缓存。进入你自己,但大多数请求很好是这样的:

return Response.success(myDecodedObject,HttpHeaderParser.parseCacheHeaders(response));


Volley调用parseNetworkResponse()从一个工作线程。这确保了耗时的解析操作,如JPEG解码成位图,不要阻塞UI线程。


4.1.2 deliverResponse

Volley 将parseNetworkResponse()返回的对象回调给主线程。大多数请求调用回调接口,例如:
protected void deliverResponse(T response) {
        listener.onResponse(response);

4.1.3 实例: GsonRequest

Gson是一个Library,使用反射将Java对象转换为JSON。您可以定义Java对象,具有相同的名称作为相应的JSON键,通过Gson类对象,Gson将为您填写的字段。下面是一个完整的实施一连串要求使用Gson解析:
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 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,
            Listener<T> listener, 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));
        }
    }
}

Volley 提供现成的JsonArrayRequest和JsonArrayObject类如果你愿意采取这种方法。有关更多信息,请参见使用标准的请求类型



原文地址: https://developer.android.com/training/volley/index.html