赞
踩
网络请求是互联网开发的基石,单机的应用终究不能发挥互联网的优势,互联网+任何东西都有不小的发展潜力。在Android开发领域,基本的网络连接主要涉及到数据的上传、下载与网页浏览,而其中数据的上传和下载是APP和服务器交互的基石,目前主要是通过HTTP协议上传和下载文本、XML、Json、图片、音视频文件,基本实现方式主要涉及HttpURLConnection和HttpClient。
本文已收录至☞Android学习路线_梳理
上一篇☞Android学习路线_入门篇(十)数据持久化之数据库
实际开发中我们和服务器交互一般用的都是基于Http协议的通信,所以学好Http协议是非常重要的,当然,我们不用过于考究一些细节的东西,有个大体的了解即可,都是一些概念性的东西。
Http全称hypertext transfer protocol(超文本传输协议),TCP/IP协议的一个应用层协议,用于定义客户端与服务器之间交换数据的过程。客户端连上服务器后,若想获得服务器中的某个资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与服务器通讯的格式。
Http操作的流程:

实际开发中我们用得较多的方式是Get和Post,但是实际开发可能还会用到其他请求方式,所有的请求方式如下:
Get:请求获取Request-URI所标识的资源POST:在Request-URI所标识的资源后附加新的数据HEAD请求获取由Request-URI所标识的资源的响应信息报头PUT:请求服务器存储一个资源,并用Request-URI作为其标识DELETE:请求服务器删除Request-URI所标识的资源TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断CONNECT:保留将来使用OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项用得最多的Get和Post对比如下:
GET:在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔, 但数据容量通常不能超过2K,比如:http://xxx?username=…&pawd=…这种就是GETPOST: 这个则可以在请求的实体内容中向服务器发送数据,传输没有数量限制Get和Post都是发送数据的,只是发送机制不一样,不要相信网上说的 “Get获得服务器数据,Post向服务器发送数据”。另外Get发送数据用的明文,安全性非常低,Post发送数据的安全性较高,但是Get执行效率却比Post方法好,一般查询的时候我们用Get,数据增删改的时候用Post!HTTP请求头信息对照表:
| Header | 解释 | 示例 |
|---|---|---|
| Accept | 指定客户端能够接收的内容类型 | Accept: text/plain, text/html |
| Accept-Charset | 浏览器可以接受的字符编码集。 | Accept-Charset: iso-8859-5 |
| Accept-Encoding | 指定浏览器可以支持的web服务器返回内容压缩编码类型。 | Accept-Encoding: compress, gzip |
| Accept-Language | 浏览器可接受的语言 | Accept-Language: en,zh |
| Accept-Ranges | 可以请求网页实体的一个或者多个子范围字段 | Accept-Ranges: bytes |
| Authorization | HTTP授权的授权证书 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
| Cache-Control | 指定请求和响应遵循的缓存机制 | Cache-Control: no-cache |
| Connection | 表示是否需要持久连接。 | (HTTP 1.1默认进行持久连接) |
| Cookie | HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。 | Cookie: $Version=1; Skin=new; |
| Content-Length | 请求的内容长度 | Content-Length: 348 |
| Content-Type | 请求的与实体对应的MIME信息 | Content-Type: application/x-www-form-urlencoded |
| Date | 请求发送的日期和时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
| Expect | 请求的特定的服务器行为 | Expect: 100-continue |
| From | 发出请求的用户的Email | From: user@email.com |
| Host | 指定请求的服务器的域名和端口号 | Host: www.zcmhi.com |
| If-Match | 只有请求内容与实体相匹配才有效 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
| If-Modified-Since | 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 | If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
| If-None-Match | 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
| If-Range | 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag | If-Range: “737060cd8c284d8af7ad3082f209582d” |
| If-Unmodified-Since | 只在实体在指定时间之后未被修改才请求成功 | If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT |
| Max-Forwards | 限制信息通过代理和网关传送的时间 | Max-Forwards: 10 |
| Pragma | 用来包含实现特定的指令 | Pragma: no-cache |
| Proxy-Authorization | 连接到代理的授权证书 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
| Range | 只请求实体的一部分,指定范围 | Range: bytes=500-999 |
| Referer | 先前网页的地址,当前请求网页紧随其后,即来路 | Referer: http://blog.csdn.net/coder_pig |
| TE | 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 | TE: trailers,deflate;q=0.5 |
| Upgrade | 向服务器指定某种传输协议以便服务器进行转换(如果支持) | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
| User-Agent | User-Agent的内容包含发出请求的用户信息 | User-Agent: Mozilla/5.0 (Linux; X11) |
| Via | 通知中间网关或代理服务器地址,通信协议 | Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) |
| Warning | 关于消息实体的警告信息 | Warn: 199 Miscellaneous warning |
HTTP响应头信息对照表:
| Header | 解释 | 示例 |
|---|---|---|
| Accept-Ranges | 表明服务器是否支持指定范围请求及哪种类型的分段请求 | Accept-Ranges: bytes |
| Age | 从原始服务器到代理缓存形成的估算时间(以秒计,非负) | Age: 12 |
| Allow | 对某网络资源的有效的请求行为,不允许则返回405 | Allow: GET, HEAD |
| Cache-Control | 告诉所有的缓存机制是否可以缓存及哪种类型 | Cache-Control: no-cache |
| Content-Encoding | web服务器支持的返回内容压缩编码类型 | Content-Encoding: gzip |
| Content-Language | 响应体的语言 | Content-Language: en,zh |
| Content-Length | 响应体的长度 | Content-Length: 348 |
| Content-Location | 请求资源可替代的备用的另一地址 | Content-Location: /index.htm |
| Content-MD5 | 返回资源的MD5校验值 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
| Content-Range | 在整个返回体中本部分的字节位置 | Content-Range: bytes 21010-47021/47022 |
| Content-Type | 返回内容的MIME类型 | Content-Type: text/html; charset=utf-8 |
| Date | 原始服务器消息发出的时间 | Date: Tue, 15 Nov 2010 08:12:31 GMT |
| ETag | 请求变量的实体标签的当前值 | ETag: “737060cd8c284d8af7ad3082f209582d” |
| Expires | 响应过期的日期和时间 | Expires: Thu, 01 Dec 2010 16:00:00 GMT |
| Last-Modified | 请求资源的最后修改时间 | Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT |
| Location | 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 | Location: http://blog.csdn.net/coder_pig |
| Pragma | 包括实现特定的指令,它可应用到响应链上的任何接收方 | Pragma: no-cache |
| Proxy-Authenticate | 它指出认证方案和可应用到代理的该URL上的参数 | Proxy-Authenticate: Basic |
HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序。 虽然HttpURLConnection的API提供的比较简单,但是同时这也使得我们可以更加容易地去使 用和扩展它。继承至URLConnection,抽象类,无法直接实例化对象。通过调用openCollection() 方法获得对象实例,默认是带gzip压缩的。
HttpUrlConnection是JDK里提供的联网API,我们知道Android SDK是基于Java的,所以当然优先考虑HttpUrlConnection这种最原始最基本的API,其实大多数开源的联网框架基本上也是基于JDK的HttpUrlConnection进行的封装罢了。
使用之前,先在AndroidManifest中加上联网权限:
<uses-permission android:name="android.permission.INTERNET" />
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000); // 设置超时时间为5000毫秒,即5秒
// 根据具体情境设置请求头,可参考 1.3 HTTP请求头
conn.setRequestProperty(Content-Type, application/x-www-form-urlencoded);
public String streamToString(InputStream is) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } baos.close(); is.close(); byte[] byteArray = baos.toByteArray(); return new String(byteArray); } catch (Exception e) { Log.e(tag, e.toString()); return null; } }
conn.setRequestMethod(GET);
int code = conn.getResponseCode();
if (code == 200) {
InputStream is = conn.getInputStream();
}
// 将输入流转换成字符串
String result = streamToString(is);
conn.setRequestMethod(POST);
try {
String data = "baseData";
conn.setRequestProperty(Content-Length, data.length() + "");
// POST方式,其实就是把数据写给服务器
conn.setDoOutput(true); // 设置可输出流
OutputStream os = conn.getOutputStream(); // 获取输出流
os.write(data.getBytes()); // 将数据写给服务器
} catch (Exception e) {
e.printStackTrace();
return 网络访问失败;
}
int code = conn.getResponseCode();
if (code == 200) {
InputStream is = conn.getInputStream();
}
// 将输入流转换成字符串
String result = streamToString(is);
HttpClient是开源组织Apache提供的Java请求网络框架,其最早是为了方便Java服务器开发而诞生的,是对JDK中的HttpUrlConnection各API进行了封装和简化,提高了性能并且降低了调用API的繁琐。
Android因此也引进了这个联网框架,我们再不需要导入任何jar或者类库就可以直接使用,值得注意的是Android官方已经宣布不建议使用HttpClient了,开发的时候尽量少用吧。尽管被Google 弃用了,但是我们我们平时也可以拿HttpClient来抓下包。HttpClient 用于接收/发送Http请求/响应,但不缓存服务器响应,不执行HTML页面潜入的JS代码,不会对页面内容进行任何解析,处理。
HttpClient对象:HttpClient client = new DefaultHttpClient();
HttpGet对象,指定请求地址(带参数);HttpGet httpGet = new HttpGet(path);
HttpClient的execute(),方法执行HttpGet请求,得到HttpResponse对象;HttpResponse response = client.execute(httpGet);
HttpResponse的getStatusLine().getStatusCode()方法得到响应码;int code = response.getStatusLine().getStatusCode();
HttpResponse的getEntity().getContent()得到输入流,获取服务端写回的数据。if (code == 200) {
InputStream is = response.getEntity().getContent(); // 获取实体内容
String result = streamToString(is); // 字节流转字符串
}
HttpClient对象;HttpClient client = new DefaultHttpClient();
HttpPost对象,指定请求地址;HttpPost httpPost = new HttpPost(path);
List,用来装载参数;List<namevaluepair> parameters = new ArrayList<namevaluepair>();
parameters.add(new BasicNameValuePair(data, "baseData"));
HttpPost对象的setEntity()方法,装入一个UrlEncodedFormEntity对象,携带之前封装好的参数;httpPost.setEntity(new UrlEncodedFormEntity(parameters, UTF-8));
HttpClient的execute()方法执行HttpPost请求,得到HttpResponse对象;HttpResponse response = client.execute(httpPost);
HttpResponse的getStatusLine().getStatusCode()方法得到响应码;int code = response.getStatusLine().getStatusCode();
HttpResponse的getEntity().getContent()得到输入流,获取服务端写回的数据;if (code == 200) {
InputStream is = response.getEntity().getContent();
String result = streamToString(is);
}
上面的请求中最后获得的数据格式都是字符串,日常开发中不可能只有纯文本数据,常见的其他数据主要有XML、JSON、图片、音视频文件。其实图片和音视频文件都属于文件,只需要将响应中获取的输入流写入目标格式的文件就完成了。XML和JSON属于特殊的文本数据,通常需要经过再次解析才能使用。
常见的XML解析方式有SAX、DOM、PULL三种:

在Android系统中内置了PULL解析器,常用的SharedPreference就是XML格式的数据并且通过PULL完成数据解析,这里就不对SAX和DOM进行详细介绍了,主要介绍一下PULL解析器的使用。
// 首先需要一个实体类Person class Person{ private String id; private String name; private String age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } } // 将XML数据转换成Person列表 public static ArrayList<Person> getPersons(InputStream xml)throws Exception{ ArrayList<Person> persons = null; Person person = null; // 创建一个xml解析的工厂 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); // 获得xml解析类的引用 XmlPullParser parser = factory.newPullParser(); parser.setInput(xml, "UTF-8"); // 获得事件的类型 int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_DOCUMENT: persons = new ArrayList<Person>(); break; case XmlPullParser.START_TAG: if ("person".equals(parser.getName())) { person = new Person(); // 取出属性值 int id = Integer.parseInt(parser.getAttributeValue(0)); person.setId(id); } else if ("name".equals(parser.getName())) { String name = parser.nextText();// 获取该节点的内容 person.setName(name); } else if ("age".equals(parser.getName())) { int age = Integer.parseInt(parser.nextText()); person.setAge(age); } break; case XmlPullParser.END_TAG: if ("person".equals(parser.getName())) { persons.add(person); person = null; } break; } eventType = parser.next(); } return persons; } // 将Person列表存为XML文件 public static void save(List<Person> persons, OutputStream out) throws Exception { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(out, "UTF-8"); serializer.startDocument("UTF-8", true); serializer.startTag(null, "persons"); for (Person p : persons) { serializer.startTag(null, "person"); serializer.attribute(null, "id", p.getId() + ""); serializer.startTag(null, "name"); serializer.text(p.getName()); serializer.endTag(null, "name"); serializer.startTag(null, "age"); serializer.text(p.getAge() + ""); serializer.endTag(null, "age"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endDocument(); out.flush(); out.close(); }
XML数据已经很少用于网络请求的数据了,现在主流的是JSON数据,这里介绍一下原生的JSONObject和JSONArray,第三方的库感兴趣的可以自行了解,比较常用的有Gson、Fastjson、Jackson。
还是基于4.1中的实体类Person进行分析,Json数据的基本格式示例:
[
{ "id":"1","name":"AAA","age":"18" },
{ "id":"2","name":"BBB","age":"18" },
{ "id":"3","name":"CCC","age":"18" }
]
JSONObject类主要用来解析一个Json数据单位,常用的方法有:
JSONObject类主要用来解析一组Json数据,得到一个JSONObject数组,常用的方法有:
简单Json数据解析使用示例:
private void parseEasyJson(String json){
persons = new ArrayList<Person>();
try{
JSONArray jsonArray = new JSONArray(json);
for(int i = 0;i < jsonArray.length();i++){
JSONObject jsonObject = (JSONObject) jsonArray.get(i);
Person person = new Person();
person.setId(i+"");
person.setName(jsonObject.getString("name"));
person.setAge(jsonObject.getString("age"));
persons.add(person);
}
}catch (Exception e){e.printStackTrace();}
}
今天的分享就到这里,文章多有不足,各位小伙伴有什么想法可以直接评论或是私信,要是对你有所帮助就给我一个赞吧,喜欢我的小伙伴可以关注我哦~
本文已收录至☞Android学习路线_梳理
上一篇☞Android学习路线_入门篇(十)数据持久化之数据库
支持我的小伙伴们可以微信搜索“Android思维库”,或者微信扫描下方二维码,关注我的公众号,每天都会推送新知识~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。