网络编程总结(九):Volley 官方文档

什么是 Volley

Volley 是一个 HTTP 库,它使得 Android 应用更容易、更快的去使用网络传输数据,它在 Github 上的地址是:https://github.com/google/volley

Volley 的优点有:

  • 自动调度网络请求

  • 多并发网络连接

  • 透明的硬盘和内存缓冲响应符合标准的 HTTP 缓存一致性

  • 支持请求优先级

  • 取消请求 API 可以使得我们取消单个请求或者指定要取消块或者范围

  • 方便定制,例如重试和退回

  • 强排序,可以轻松的使用异步网络获取数据来填充 UI

  • 调试和跟踪工具

Volley 擅长的的 RPC 类型的操作过去常常应用于填充UI,例如提取一页的搜索结果作为结构化数据。它可以很方便的和其他协议整合在一起,并提供支持原始字符串、图像和 JSON。通过需要的功能提供内置支持,Volley 可以从样板式代码中解脱出来,使得你可以更专注于你应用的逻辑细节中。

Volley 不适合大型下载或者流式操作,因为 Volley 在解析期间会在内存中保存所有响应数据。对于大型下载操作,请考虑使用 DownloadManager

Volley 的核心库是在 Github 上开发的,它包含主要的请求分发管道和一组可以在 “Volley 工具箱”中使用的常用工具。将 Volley 添加到项目只能够的最简单的方法是将一下的依赖项添加到 APP 的 build.gradle 文件中:

dependencies {
    ...
    compile 'com.android.volley:volley:1.0.0'
}

你还可以克隆 Volley 存储库并将其设置为库项目:

  1. 在命令行中键入以下命令来克隆存储库
git clone https://github.com/google/volley
  1. 创建 Android 库所示将下载的来源作为 Android 库模块导入到你的 APP 中。

Google I/O 大会上介绍 Volley 的视频:

发送一个简单的请求(Sending a Simple Request)

在一个高级别中,你使用 Volley 通过创建一个 RequestQueue 并如一个 Request 对象,RequestQueue 负责管理用于网络操作、读写缓存以及解析响应的工作线程。Requests 对原始响应进行解析,Volley 负责将解析的响应分发给主线程进行传递。

本课介绍如何使用 Volley.newRequestQueue 方法发送请求,该方法为您设置了 RequestQueue。有关如何设置 RequestQueue 的信息,请参阅下一课“设置 RequestQueue”。

本课还介绍如何像 RequestQueue 添加请求并取消请求。

添加网络权限

要使用 Volley,你必须在你的应用的清单文件中添加 android.permission.INTERNET 权限,否则,你的 APP 将无法连接网络。

使用 newRequestQueue

Volley 提供了一个便捷的方法 Volley.newRequestQueue 来帮你设置一个 RequestQueue,使用默认值和启动队列,例如:

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

Volley 将已经解析的响应提供给主线程。在主线程上运行的好处是方便用接收到的数据填充 UI控件,因为你可以从响应处理程序直接自由的修改 UI,但是类库提供的语义格外的重要,尤其是关联到取消请求时。

有关如何自己设置 RequestQueue 的描述,在后面会讲解,而不是使用 Volley.newRequestQueue。

发送一个请求(Send a Request)

要发送请求,您只需构造一个请求,并使用 add() 方法将其添加到 RequestQueue 中去,就和上面的例子一样。一旦你添加了请求,它将移动到管道,获取服务,并解析和传递它的原始响应。

当调用 add() 方法时,Volley 会启用一个缓存处理线程和一个网络分发线程池。当你向 RequestQueue 中添加请求时,请求由缓存线程挑选并分类:如果缓存可以为该请求服务,缓存响应则在缓存线程中解析,并且解析的响应在主线程上传递。如果不能由缓存服务,则将其放置到网络队列上去。第一个可用的网络线程从队列获取该请求,进行 HTTP 传输事务,在工作线程中解析响应的内容,将响应写入缓存,并将解析后的响应发送到主线程中。

注意,像阻塞 I/O 和编码/解码这样耗时的操作是在工作线程上完成的。你可以从任何线程添加请求,但响应总是会被传递到主线程上。

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

取消请求(Cancel a Request)

要取消一个请求,请调用 Request 对象的 cancel() 方法。一旦请求取消,Volley 保证你的响应处理器不会被调用。这意味着你可以在 Activity 的 onStop() 方法中取消所有的待处理请求,并且不必对 getActivity == null 进行检查来处理响应处理器,无论 onSaveInstanceState() 是否已被调用,或者其他自定义的代码。

要利用此行为,通常必须跟踪所有正在运行的请求,以便能够在适当的时间取消它们。有一个更简单的方法:你可以将标记对象和每个请求相关联。然后,你可以使用该标记来提供要取消的请求的范围。例如,你可以使用他们代表的 Activity 来标记所有请求,并从 onStop() 调用 requestQueue.cancelAll(this)。同样,你也可以在一个 ViewPager 选项卡中,使用他们各自的选项卡对象标记他们自己的所有缩略图请求,并在切换时出发终止操作,以确保新的选项卡对象不被其他选项卡的请求所持有。

下面是一个使用字符串值作为标记的示例:

  1. 定义你的标签并将其添加到你的请求中:
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);
  1. 在你的 Activity 的 onStop() 方法中取消所有具有该标记的请求:
@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

取消请求的时候请小心。如果你依赖你的响应处理器来改变一个状态或者跳过一些步骤,你需要记得这些。再次强调,在终止后相应处理绝不会被调用。

设置一个请求队列(Setting Up a RequestQueue)

以前的课程展示了如果使用 Volley.newRequestQueue 方法来设置一个 RequestQueue,利用 Volley 默认的行为的好处。本课程将引导你完成创建 RequestQueue 的明确步骤,以便你可以定制你自己的行为。

本课程还描述了将 RequestQueue 创建为单例的推荐做法,这使得 RequestQueue 可以持续整个应用的生命周期。

设置一个网络和缓存(Set Up a Network and Cache)

一个 RequestQueue 要完成它的工作需要两样东西:一个网络来传输请求,一个缓存来处理缓存。在 Volley toolbox 中有这两样标准的可用的实现:DiskBasedCache 提供了“一个文件一个缓存响应”并在内存中建立索引;BasicNetwork 根据你选择的 HTTP 客户端提供网络传输。

BasicNetwork 是 Volley 的默认的网络实现。必须使用你的应用的 HTTP 客户端来初始化 BasicNetwork 才能连接到网络。通常这是一个 HttpURLConnection。

下面的代码列出了设置 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 ="http://www.example.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 创建为单例模式,可以使得它和你的应用的生命周期一致,详情见下一节。

使用单例模式(Use a Singleton Pattern)

如果你的应用程序需要不断的使用网络,可能最有效的方法是设置一个单例模式的 RequestQueue,这使得它一直持续于你的应用的生命周期之中。你可以通过各种方法实现。推荐的方法是封装 RequestQueue 和其他 Volley 功能的单例类。另一种方法是继承 Application 并在 Application.onCreate() 中设置 RequestQueue。但不鼓励这种方法;静态单例可以以更模块化的方式提供同样的功能。

一个关键的概念是 RequestQueue 必须使用 Application context 来初始化对象,而不是使用 Activity context。这样可以确保 RequestQueue 在你的应用程序生命周期中持续,而不是每次重新创建 Activity 时重新创建 RequestQueue(例如,当用户旋转设备时)。

这是一个提供 RequestQueue 和 ImageLoader 功能的单例类:

public class MySingleton {
    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;
    }
}

下面的例子使用单例类来执行 RequestQueue 操作:

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

创建一个标准的 Request(Making a Standard Rquest)

本课介绍如何使用 Volley 支持的常见请求类型:

  • 字符串请求:指定 URL 并接收原始字符串作为响应。请参阅“设置一个请求队列”中的例子。

  • JSON 对象和 JSON 数组(JsonRequest 的两个子类):执行 URL 并分别获取 JSON 对象或者 JSON 数组。

如果你的预期响应是这几种其中之一,那么可能不需要实现自定义请求。本课介绍如何使用这些标准请求。有关如何实现自定义请求,会在下面讲到。

请求 JSON(Request JSON)

Volley 为 JSON 请求提供一下的类:

  • JsonArrayRequest : 在特定 URL 检索 JSONArray 响应的请求。

  • JsonObjectRequest : 在特定 URL 检索 JSONObject 响应的请求,允许将可选的 JSONObject 作为请求提的一部分传入。

这两个类都是基于 JsonRequest 的。他们的使用方式和其他类型的请求相同。例如,下面的代码将或者 JSON Feed,并将其在 UI 中显示。

TextView mTxtDisplay;
ImageView mImageView;
mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);
String url = "http://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);

有关实现基于 Gson 的自定义 JSON Request 的示例,下面的内容会讲到。

实现自定义请求(Implementing a Custom Request)

本科介绍如何实现自定义请求,Volley支持的 out-of-the-box 类型的没有包含这些类。

写一个自定义请求(Write a Custom Rquest)

工具盒中的大多数请求都是即用型的;如果你的响应是字符串、图片或者 JSON,则不需要去自定义请求。

对于需要去自定义的请求,需要执行以下操作:

  • 扩展 Request<T> 类,其中 <T> 表示请求期望的响应类型。因为,如果你的响应是字符串,例如,通过扩展 Request<String> 创建自定义类型。有关扩展 Request<T> 的例子,请参与 Volley toolbox 中的 StringRequest 类 和 ImageRequest 类。

  • 实现抽象方法 parseNetworkResponse() 和 deliverResponse(),下面有更详细的描述。

parseNetworkResponse

对于指定的类型(例如字符串、图像或JSON),response 封装了用于传递的解析后的响应。下面是 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
...
}

注意:

  • parseNetworkResponse() 有一个 NetworkResponse 参数,它包含了字节数组的响应、HTTP 状态码和响应头。

  • 你的实现必须返回 Response<T>,其中包含响应对象和缓存元数据或者解析失败后的错误信息。

如果你的协议具有非标准的缓存语义,你可以自定构建一个 Cache.Entry,但大多数请求都是这样的:

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

Volley 在工作线程中调用 parseNetworkResponse()。这样做可以确保耗时的操作(例如将 JPEG 解码为 Bitmap)不会阻塞 UI 线程。

deliverResponse

Volley 在主线程回调 parseNetworkResponse() 方法返回的对象,大多数请求在这里调用回调接口,例如:

protected void deliverResponse(T response) {
        listener.onResponse(response);
例子:Gson 请求(Gson Request)

Gson 是使用反射将 Java 对象和 JSON之间进行转换的库。你可以定义一个和那些 JSON 的键名称具有相同名称的字段的 Java 对象,将 Gson 传给该类对象,Gson 将为你填写字段。下面是使用 Gson 进行解析的 Volley 请求的完整实现:

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 类。有关详细信息,可以参阅之前讲解的内容。

Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1