Android应用开发:网络工具——Volley(一)

引言

网络一直是我个人的盲点,前一阵子抽出时间学习了一下Volley网络工具的使用方法,也透过源码进行了进一步的学习,有一些心得想分享出来。在Android开发中,成熟的网络工具不少,Android自带了HttpClient,还有okhttp,还有koush大神创建的ion开源项目,然后就是google后来加入到Android项目源码中的Volley。为什么使用Volley,是因为Volley使用简单,逻辑清晰,即使在调试过程中出现了问题,也可以快速的通过源码进行定位。

Volley编译

因为已经习惯了使用Gradle构架应用,所以我在第一次想要使用Volley的时候尝试寻找是否可以通过gradle的配置文件进行库依赖。可惜的是,并没有。但即使这样Volley的库也很容易做出来加入到我们的工程中。

首先需要ant编译工具,然后如果有Android系统源码的话,Volley在frameworks/volley目录下。如果没有Android源码,也很好办,可以单独从Android的仓储中克隆出Volley源码:

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

不幸的是,volley库的源码Android并没有托管在其在Github的帐号上,所以只能在googlesource上进行克隆,当然在国内也就需要先FQ才可以了。

下图为Volley源码结构:

克隆成功后,可以方便的使用ant进行编译,当然,如果是在完整的Android源码下,也可以直接通过make进行编译,但是时间必然会长很多。这里使用ant编译为例,执行:

ant jar

结果如图所示:

这样jar包就生成了,很方便吧,接下来将其添加到工程中就可以使用了。

Volley使用

Volley的网络请求父类为Request<T>,可以提供给开发者进行继承,同时也预置了几种开发中常用的请求类型,下边介绍两个:StringRequest和JsonObjectRequest。

为了更加贴近实际使用,下边将使用Volley与Cloudant进行通讯做示例。Cloudant是一家提供云服务业务的公司,其向开发者提供免费的云存储、云数据库服务。关于其注册等流程本文不做叙述,很简单的。直接从登录开始:

1. 申请网络请求队列

Volley的一个很大的特色,就是所有的网络请求无需开发者自己执行,而是在请求构造完成后扔到Volley的请求队列中,队列依次进行请求,这样就省去了很多麻烦,开发者也不用担心网络请求是否会冲突,是否会在主线程,这些烦心事Volley的网络队列都帮我们解决了。

一般来说,一个应用程序如果网络请求没有特别频繁则完全可以只有一个请求队列(对应Application),如果非常多或其他情况,则可以是一个Activity对应一个网络请求队列,具体情况具体分析。下边的代码展示了如何申请一个Volley网络请求队列:

RequestQueue mQueue;
mQueue = Volley.newRequestQueue(getApplicationContext());

这样就成功申请了一个网络请求队列,如果只有一个,则可以在Application中进行申请。

2. 使用Volley登录Cloudant

假设已经成功注册,登录名foo,密码bar。

通过查阅Cloudant的登录认证文档:https://docs.cloudant.com/api/authn.html。可以发现Cloudant登录认证相关接口有三个:

这里我们使用POST方法进行cookie登录认证。结合上边假设的用户名和密码可知:

要访问的url为 foo.cloudant.com/_session
头信息为 Content-Type: application/x-www-form-urlencoded
参数为 name = foo, password = bar

若访问成功,我们就可以在网络回应中获取cookie,以备之后其他操作使用。显然,这个请求跟json毫无关系,应该使用StringRequest,StringRequest有两种构造方法:

public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)

public StringRequest(String url, Listener<String> listener, ErrorListener errorListener)

第二个方法只有GET请求才可以使用,第一个方法的method参数可以用来自定义请求类型,这里我们需要的是POST,所以应该使用第一个构造方法:

StringRequest request = new StringRequest(
                Request.Method.POST,
                "http://foo.cloudant.com/_session",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {  //收到成功应答后会触发这里

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) { //出现连接错误会触发这里
                    }
                }
        );

上边的代码中,我们成功构造了一个StringRequest,其中已经包含了我们需要的POST和正确的URL,同时还添加了网络回应监听器。但是,还缺少文档要求我们的头信息和参数。StringRequest在构造中并不提供这些信息的定义,这也是与其他常用网络工具不同的地方,刚接触的同学可能会很不适用,通过复写StringRequest的两个方法就可以将这些信息放进去了。下边来完善这个请求:

StringRequest request = new StringRequest(
                Request.Method.POST,
                "http://foo.cloudant.com/_session",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {  //设置头信息
                Map<String, String> map = new HashMap<String, String>();
                map.put("Content-Type", "application/x-www-form-urldecoded");
                return map;
            }

            @Override
            protected Map<String, String> getParams() throws AuthFailureError {  //设置参数
                Map<String, String> map = new HashMap<String, String>();
                map.put("name", "foo");
                map.put("password", "bar");
                return map;
            }
        };

相比第一次我们的构造过程,这一次多了两个复写的方法来设置头信息和参数,很容易吧。这个时候请求基本完成了,但是却缺少另一个很重要的东西,我们的登录认证为的是拿回属于自己的cookie,如果不能获取cookie的话,多么正确的请求格式都是白费力气啊,想要拿到cookie一样也是通过复写另一个方法进行获取:

            @Override
            protected Response<String> parseNetworkResponse(NetworkResponse response) {
                for (String s : response.headers.keySet()) {
                    if (s.contains("Set-Cookie")) {
                        mCookie = response.headers.get(s);
                        break;
                    }
                }
                return super.parseNetworkResponse(response);
            }

在网络请求成功后,服务端返回应答信息,而我们所需的Cookie信息就在这些应答信息中,通过对应答信息的遍历查找,很方便就可以找到我们所需的信息了。到这里,我们的登录认证请求就构造完成了,最后需要做的就是将这个StringRequest扔到我们的请求队列中去:

mQueue.add(request);

网络通畅的情况下,很快就能够获取Cookie信息了。

3. 查看测试文档

在注册Cloudant成功后,Cloudant会在我们的帐号中创建一个默认数据库——crud,其中保存着一行测试数据welcome。

让我们用Volley来访问这条数据。查阅Cloudant API文档Documents相关可以发现:

通过简单的GET请求搭配正确的URL即可得到文件(数据)内容,当然,这一切的前提是我们已经掌握了正确的Cookie数据。那么,我们需要:

1. 请求头数据中包含正确的Cookie信息
2. 访问正确的URL
3. 请求类型:GET

假设通过上一步登陆认证后我们将Cookie信息保存在了mCookie字符串变量中。而我们需要访问的URL通过查阅文档也可以得出路径为 数据库名 + 文档名,即foo.cloudant.com/crud/welcome。万事俱备,使用StringRequest:

        StringRequest request = new StringRequest(
                "http://foo.cloudant.com/crud/welcome",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map = new HashMap<String, String>();
                map.put("Cookie", mCookie);
                return map;
            }
        };
        mQueue.add(request);

在onResponse中我们会收到welcome这条数据的json形式字符串:

简单的网络请求StringRequest完全处理得来,使用也比较简单,就介绍到这里。下边介绍JsonObjectRequest应用方法。

4. 使用JsonObjectRequest创建新数据

首先看一下JsonObjectRequest的构造方法:

public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)

public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)

第一种方法参数以此为:请求方法,访问的URL,Json数据对象,请求成功监听器,请求失败监听器。

第二种构造方法中,若jsonRequest为空,则方法自动为GET,不为空则自动切换为POST,其他参数含义相同。

Cloudant的文档(https://docs.cloudant.com/api/documents.html)要求创建文档可以使用POST或PUT方法进行,所携带的数据均为json格式。这样以来,StringRequest就显得力不从心了,我们需要使用到Volley的另一个自带请求类型:JsonObjectRequest。下边以POST方式创建数据为例,通过查看Cloudant文档,可知:

1. 访问的URL path为数据库目录
2. Content-Type被要求为application/json
3. 携带的数据要求为json数据

既然方法要求为POST,我们又是创建数据,肯定数据内容不会为空,所以我们选择第二种构造方法。首先,创建一个Json对象:

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("_id", "testinfo");
        jsonObject.put("person", "foo");
        jsonObject.put("phone", "bar");

在Cloudant数据存储系统中,id可以由开发者指定。接下来进行JsonObjectRequest的构造和请求:

        JsonObjectRequest request = new JsonObjectRequest(
                "http://foo.cloudant.com/crud",
                jsonObject,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject jsonObject) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map = new HashMap<String, String>();
                map.put("Cookie", mCookie);
                return map;
            }
        };
        mQueue.add(request);

jsonObject数据不为空,所以请求方式自动切换为POST,url为所要创建数据所在的数据库所在路径,然后就是请求结果的监听器,最后别忘了将Cookie带上,否则会出现认证错误的。最后,将构造完成的请求丢进队列中,由Volley进行调度处理。这个时候不妨再回头看一看之前分析的请求所需要哪些元素,不难发现,Volley的json请求中,并没有对Content-Type进行特殊设定。JsonObjectRequest是继承于JsonRequest的,而JsonRequest已经帮我们完成了这个动作:

    @Override
    public String getBodyContentType() {
        return PROTOCOL_CONTENT_TYPE;
    }

PS:设置Content-Type也可以通过复写getBodyContentType这个函数,而不用总是麻烦的使用getHeader中的map进行设定,两种设定方式效果一致。而且也不用担心编码格式,因为默认就是utf-8的:

    /** Charset for request. */
    private static final String PROTOCOL_CHARSET = "utf-8";

    /** Content type for request. */
    private static final String PROTOCOL_CONTENT_TYPE =
        String.format("application/json; charset=%s", PROTOCOL_CHARSET);

到这里,Json请求的相关用法也就介绍完了。下一节将会从源码角度分析一下Volley请求的逻辑顺序究竟是怎样的,如果我们需要书写自己的请求类型,都需要复写哪些函数,以及需要注意些什么。

源码

关于Volley和Cloudant更多的通信细节,见CloudantVolley项目:https://github.com/airk000/CloudantVolley

时间: 2024-10-08 20:38:07

Android应用开发:网络工具——Volley(一)的相关文章

Android应用开发:网络工具——Volley(二)

引言 在Android应用开发:网络工具--Volley(一)中结合Cloudant服务介绍了Volley的一般使用方法.当中包括了两种请求类型StringRequest和JsonObjectRequest.一般的请求任务相信都能够通过他们完毕了,只是在千变万化的网络编程中,我们还是希望能够对请求类型.过程等步骤进行全然的把控.本文就从Volley源代码角度来分析一下.一个网络请求在Volley中是怎样运作的.也能够看作网络请求在Volley中的生命周期. 源头RequestQueue 在使用V

Android框架之网络开发框架Volley

1. Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Android

Android应用开发-网络编程(一)(重制版)

网络图片查看器 1. 确定图片的网址 2. 发送http请求 URL url = new URL(address); // 获取客户端和服务器的连接对象,此时还没有建立连接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 设置请求方法,注意必须大写 conn.setRequestMethod("GET"); // 设置连接和读取超时 conn.setConnectTimeout(5000); c

Android|Java 开发常用工具类

如题 该文章展示的是我开发过程中使用的部分常用工具类方法,不定期更新. 欢迎各位大牛批评指教,如有发现错误,欢迎留言指教,如有更好的实现方式,也欢迎留言交流学习,谢谢. 一.手机号 座机号.邮箱格式匹配工具类 package com.kevin.test.utils; /** * 字符串格式匹配工具类 匹配手机号.座机号.邮箱等 * * @author blj * */ public class FormatCheckUtils { /** * 判断是否符合邮箱格式 */ public stat

Android应用开发-网络编程(二)(重制版)

Apache HttpClient框架 GET方式请求提交数据 1. 创建一个HttpClient HttpClient hc = new DefaultHttpClient(); 2. 创建一个HttpGet,要提交给服务器的数据已经拼接在path中 HttpGet hg = new HttpGet(path); 3. 使用HttpClient对象发送GET请求,建立连接,返回响应头对象 HttpResponse hr = hc.execute(hg); 4. 拿到响应头中的状态行,获取状态码

Android应用开发高效工具集1---ant构建简单Android项目

在java编译那些事儿中提到了用ant去编译Java项目,今天扩展到用它来构建Android项目,其实道理是相通的,变化的只是使用的形式.ant构建相比IDE的好处是多个子项目使用自定义jar包时,ant可以更好的完成自动化构建,一个命令就搞定整个项目的编译而不用手工的导出jar包然后再将其放到指定目录.这就是高效的构建工具所标榜的. 先来说说ant在Linux下的安装(那篇文章写的太过简单,其实也是很简单的,别看mannul中写的那么复杂). 1.ant的安装 最简单的办法就是直接用Linux

Android SDK开发常用工具的使用及其异常处理

由于以下操作都是命令操作,所以在执行以下操作之前确保环境变量 ANDROID_HOME 指向的是正确的Android SDK的路径: 启动Android SDK Manager: android 启动Android Device Monitor: monitor 启动UI Automator Viewer: uiautomatorviewer 有的时候启动UI Automator Viewer失败,会提示“unexpected error while parsing input invalid

android客户端访问网络工具类

package com.yqq.loginclient.utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.HttpURLConnection; import java.net.MalformedURLExceptio

配置Android应用开发环境

一.安装JDK 开发 Android应用程序的时候,仅有Java运行环境(Java Runtime Environment,JRE)是不够的,需要完整的JDK(JDK包含了JRE),且要求其版本在JDK 6以上,在开发Android 5及更高版本时,需要JDK 7及其以上版本. 如果JDK不可用或版本低于JDK 6,要下载Java SE开发工具包7 . 使用JDK 7及以上版本无需再对环境变量进行设置. 若安装JDK 6,需要在cmd下使用Java命令和编译.运行程序,可以配置环境变量(具体步骤