«

《深入理解Android网络编程》第3章

时间:2024-3-2 20:06     作者:韩俊     分类: Android


http://www.apkbus.com/android-140261-1-1.html

请求
GET 请求获取Request-URI所标识的资源
POST 在Request-URI所标识的资源后附加新的数据

GET方式提交的数据最多只能有2048字节(URL+参数),而POST则没有此限制。
官方说明:http://support.microsoft.com/default.aspx?scid=kb;EN-US;q208427

http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
 Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

200 OK 客户端请求成功

3.2 Android中的HTTP编程
3.2.1 HttpClient和URLConnection
HTTP协议是现在Internet上使用得最多、也是最重要的协议之一,越来越多的Android应用程序需要直接通过HTTP协议来访问网络资源。虽然在Android的java.net包中已经提供了访问HTTP协议的基本功能,但是对于大部分应用程序来说,Android原生提供的功能还不够丰富和灵活。HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且支持HTTP协议最新的版本和建议。
一般情况下我们使用浏览器来访问一个Web服务器,用来浏览页面查看信息或者提交一些数据等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及通过加密方式传输,如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过用户可能在某些时候想通过自己的程序来使用别人所提供的服务页面,比如从网页中“抓取”一些数据;利用某些站点提供的页面来完成某种功能等,例如通过已有网站提供的服务去查看某个手机号码的归属地。考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及COOKIE的处理问题。这些问题有了HttpClient就很容易解决了!HttpClient就是专门设计用来简化HTTP客户端与服务器间各种通信编程的。通过它可以让原来很复杂的事情现在轻松解决。
URL(Uniform Resource Locator)代表统一资源定位符,Internet上的每个资源都具有一个唯一的名称标识,通常称为URL地址,这种地址可以是本地磁盘,也可以是局域网上的某一台计算机,更多的是Internet上的站点,因此URL是指向互联网“资源”的指针。URLConnection则代表了应用程序和URL之间的通信链接,通过URLConnection类的实例可以读取和写入此URL应用的资源。本节后面会给出使用URLConnection获取网络信息的实例。

3.2.2 Post和Get在HttpClient的使用
HttpClient提供的主要的功能如下:
实现了所有HTTP的方法(GET、POST、PUT、HEAD等)
支持自动转向
支持HTTPS协议
支持代理服务器
HTTP请求方法中最常用的是GET方法和POST方法。
1)GET方法
GET方法要求服务器将URL定位的资源放在响应报文的数据部分,回送给客户端。使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始。

// 通过GET方法获取页面信息
// 参数为对应页面的URL
public static InputStream getInputStreamFromUrl(String url) {
// 定义输出流变量
InputStream content = null;
try {
      // 取得默认的HttpClient实例
      HttpClient httpclient = new DefaultHttpClient();
      // 连接到服务器
      HttpResponse response = httpclient.execute(
      // 创建HttpGet实例
      new HttpGet(url));
      // 获取数据内容
      content = response.getEntity().getContent();
      } catch (Exception e) {
      }
      // 以InputStream形式返回页面信息
      return content;
}

上面的GET方法是以InputStream的形式返回页面的信息,很多情况下需要以String-Builder、String等字符串的格式。下面的方法把InputStream格式转为StringBuilder和String格式。

// 将InputStream格式转化为StringBuilder格式
private StringBuilder inputStreamToStringBuilder (InputStream is) {
      // 定义空字符串
      String line = "";
      // 定义StringBuilder的实例total
      StringBuilder total = new StringBuilder();
      // 定义BufferedReader,载入InputStreamReader
      BufferedReader rd = new BufferedReader(new InputStreamReader(is));
      // readLine是一个阻塞的方法,当没有断开连接的时候就会一直等待,直到有数据返回
      //  返回null表示读到数据流最末尾
      while ((line = rd.readLine()) != null) { 
                total.append(line); 
      }
      // 以StringBuilder形式返回数据内容
      return total;
}
// 将InputStream格式数据流转换为String类型
private String inputStreamToString(InputStream is) {
      // 定义空字符串
      String s = "";
      String line = "";
      // 定义BufferedReader,载入InputStreamReader
      BufferedReader rd = new BufferedReader(new InputStreamReader(is));
      // 读取到字符串中
      while ((line = rd.readLine()) != null) {
                s += line; 
      }
      // 以字符串方式返回信息 
      return s;
}
2)POST方法
POST方法要求被请求服务器接收附在请求后面的数据,常用于提交表单。当客户端给服务器提供信息较多时可以使用POST方法。POST方法将请求参数封装在HTTP请求数据中,以名称值的形式出现,可以传输大量数据。
public void postData() {
// 创建一个新的HttpClient Post头
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http:// www.google.com");
try {
// 添加数据
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("id", "12345"));
nameValuePairs.add(new BasicNameValuePair("stringdata", "myString"));
// 使用utf-8格式对数据进行编码
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs ,"UTF-8"));
// 执行 HTTP Post 请求
HttpResponse response = httpclient.execute(httppost);
} catch (ClientProtocolException e) { 
} catch (IOException e) { 
}
}

使用HttpClient需要以下6个步骤:
步骤1 创建HttpClient的实例。
步骤2 创建某种连接方法的实例,对于get方法是GetMethod,而对于post方法是PostMethod。
步骤3 调用步骤1中创建好的实例的execute方法来执行步骤2中创建好的method实例。
步骤4 读response。
步骤5 释放连接。
步骤6 对得到的内容进行处理。

3.2.3 实战案例:使用HttpClient和URLConnection访问维基百科/////

3.3 Android处理JSON
3.3.1 JSON简介
JSON指的是JavaScript对象表示法(JavaScript Object Notation),它是一种轻量级的文本数据交换格式,类似于XML,但比XML更小、更快、更易解析。
JSON是基于JavaScript的一个子集,它使用JavaScript语法来描述数据对象,但是JSON仍然独立于语言和平台。JSON解析器和JSON库支持许多不同的编程语言。这些特性都使得JSON成为理想的数据交换语言,使其易于人们阅读和编写,同时也易于机器解析和生成。
JSON的优点如下:
数据格式比较简单,易于读写,格式都是压缩的,占用带宽小。
易于解析语言,客户端JavaScript可以简单地通过eval()进行JSON数据的读取。
支持多种语言,包括ActionScript、C、C#、ColdFusion、Java、JavaScript、Perl、PHP、Python、Ruby等服务器端语言,便于服务器端的解析。
众多服务器端的对象、数组等能够直接生成JSON格式,便于客户端的访问提取。
因为JSON格式能够直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,但是完成的任务不变,且易于维护。
JSON用于描述数据结构,有以下两种形式。
(1)“名称/值”对的集合(A collection of name/value pairs)
“名称/值”对的集合形式又称为JSON Object,其名称和值之间使用“:”隔开,一般的形式如下:
{name:value}
例如:{ “Width”:”800” , “Height”:”600” }
其中名称是字符串;值可以是字符串、数值、对象、布尔值、有序列表或者null值。字符串是以”“括起来的一串字符;数值是一系列0~9的数字组合,可以为负数或者小数,还可以用e或者E表示为指数形式;布尔值表示为true或者false。
上述是以“{”开始,并以“}”结束的一系列非排序的“名称/值”对(每个“名称/值”对之间使用“,”分隔)。不同的语言中,这种“名称/值”可以理解为对象(object)、记录(record)、结构(struct)、字典(dictionary)、哈希表(hash table)、有键列表(keyed list)或者关联数组(associative array)等。

(2)值的有序列表(An ordered list of values)
值的有序列表形式又称为JSON Array。在大部分语言中,值的有序列表被理解为数组(array),一个或者多个值用“,”分隔后,使用“[”和“]”括起来就形成了这样的列表,如下所示:
[collection, collection]
例如:
{
“employees”: [
{ “Width”:”800” , “Height”:”600” },
{ “Width”:”700” , “Height”:”800” },
{ “Width”:”900” , “Height”:”900” }
]
}

3.3.2 JSON数据解析
解析JSON数据时,首先需要明确待解析的是JSON Object还是JSON Array,然后再解析。举例如下。
(1)解析JSON Object之一
下面是一个简单的JSON Object,name为名称,Lili是name的值,将name和Lili用“:”隔开,其文本如下。
{“name”:”Lili”}
JSONObject.getString(”String”)方法可以得到JSON对象中String名称对应的值。下面是对上面JSON对象的解析方法:
// 新建JSONObject, jsonString字符串中为上面的JSON对象的文本
JSONObject demoJson = new JSONObject(jsonString);
// 获取name名称对应的值
String s = demoJson.getString(“name”);
(2)解析JSON Object之二
下面是一个包含两个“名称/值”对的JSON对象,两个“名称/值”对分别是”name1”:”android”和”name2”:”iphone”,中间使用“,”隔开,其文本如下:

{"name1":"android","name2":"iphone"}
上面JSON对象的解析方法如下:
// 新建JSONObject对象,将jsonString字符串转换为JSONObject对象
// jsonString字符串为上面的文本
JSONObject demoJson = new JSONObject(jsonString);
// 获取名称为name1对应的值
String name1= demoJson.getString("name1");
// 获取名称为name2对应的值
String name2 = demoJson.getString("name2");
(3)解析JSON Array
下面是一个简单的JSONArray,number为数组名称,[1,2,3]为数组的内容,其JSON文本表示如下:
{"number":[1,2,3]}
上面的JSON Array解析方法如下:
// 新建JSONObject对象,将jsonString字符串转换为JSONObject对象
// jsonString字符串为上面的文本
JSONObject demoJson = new JSONObject(jsonString);
// 获取number对应的数组
JSONArray numberList = demoJson.getJSONArray("number");
// 分别获取numberList中的每个值
for(int i=0; i<numberList.length(); i++){
       // 因为数组中的类型为int,所以为getInt,其他getString、getLong具有类似的用法
       System.out.println(numberList.getInt(i));
}
(4)解析JSON Object和JSON Array混合对象
下面是一个JSON Object和JSON Array的混合文本,mobile为JSON Object名称,其对应的值为JSON Array,JSON Array中包含的对象为JSON Object,其文本表示如下:
{"mobile":[{"name":"android"},{"name":"iphone"}]}
上面文本的解析方法如下:
// 新建JSONObject对象,将jsonString字符串转换为JSONObject对象
// jsonString字符串为上面的文本
JSONObject demoJson = new JSONObject(jsonString);
// 首先获取名为mobile的对象对应的值
// 该值为JSONArray,这里创建一个JSONArray对象
JSONArray numberList = demoJson.getJSONArray("mobile");
// 依次取出JSONArray中的值
for(int i=0; i<numberList.length(); i++){
      // 从第i个取出JSONArray中的值为JSON Object “名称/值”对
      // 通过getString("name")获取对应的值
      System.out.println(numberList.getJSONObject(i).getString("name"));
}
3.3.3 JSON打包
要想在客户端通过JSON传送对象,需要在JSON把信息全部“打包”之后将JSONObject转换为String。这样JSON就会将“打包”的信息按照特定标准的格式进行“压缩”,之后在服务端进行解析,读取通过JSON传送来的信息。
Android提供的JSON解析类都在包org.json下,主要有以下几个。
JSONObject:可以看作是一个JSON对象,这是系统中有关JSON定义的基本单元,即前面提到的“名称/值”对。
JSONStringer:JSON文本构建类,这个类可以帮助快速和方便地创建JSON文本。其最大的优点在于可以减少由于格式的错误导致的程序异常,引用这个类可以自动严格按照JSON语法规则创建JSON文本。每个JSONStringer实体只能对应创建一个JSON文本。
JSONArray:它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,数值以逗号“,”分隔,即前面提到的值的有序列表。
JSONTokener:JSON解析类。
JSONException:JSON中涉及的异常。
下面看一个用JSONObject、JSONArray来构建JSON文本的例子,其需要构建的文本如下:
{  
      "Strings" : { "Strings1" : "MyStrings", "Strings2" : "MyStrings" },
      "Number" : ["987654321", "123456789","456789123"],  
      "String" : "good", 
      "Int" : 100, 
      "Boolean" : false  
}

上面的JSON对象中包含了5个“名称/值”对,其中名称为Strings的对应的值本身也是一个包含2个“名称/值”对的JSON对象;名称为Number的对应的值为一个JSON Array;名称为String的对应的值为一个字符串;名称为Int的对应的值为一个整型;名称为Boolean的对应的值为布尔型。
构建上述文本的主要代码如下:

try {
      // 创建JSONObject对象
      JSONObject mJSONObject = new JSONObject();  
      // 为Strings创建JSONObject对象
      JSONObject Strings = new JSONObject();  
      // 为Strings JSONObject对象添加第一个“名称/值”对
      Strings.put("Strings1", " MyStrings");  
      // 为Strings JSONObject对象添加第二个“名称/值”对
      Strings.put("Strings2", " MyStrings"); 
      // 将Strings添加到mJSONObject中
      mJSONObject.put("Strings", Strings);
      // 为Number创建JSONArray对象
      JSONArray Number = new JSONArray();  
      // 将有序列表添加到Number中
      Number.put("987654321").put("123456789").put("456789123");  
      // 将Number添加到mJSONObject中
      mJSONObject.put("Number", Number);
      // 将Int“名称/值”对添加到mJSONObject中
      mJSONObject.put("Int", 100);
      // 将Boolean“名称/值”对添加到mJSONObject中
      mJSONObject.put("Boolean", false);  
} catch (JSONException ex) {  
      // 进行异常处理
      throw new RuntimeException(ex);
}

3.3.4 实战案例:JSON解析wikipedia内容
下面的实例将解析以JSON表现的wikipedia的内容。wikipedia的API网址是http://en.wikipedia.org/w/api.php。如果需要查询Android的相关内容,并用JSON格式显示出来,可以使用如下的API:
http:// en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&titles=Android&format=json
其显示的内容为:

{
    "query": {
       "pages": {
          "8325880": {
             "pageid": 8325880,
             "ns": 0,
             "title": "Android",
             "revisions": [
                {
               "*": "{{wiktionary|Android|android}}n'''Android''' commonly- refers to:n* [[Android (robot)]], designed to resemble a humann* [[Android (operating system)]], for mobile devices, produced by Googlen'''Android''' may also refer to:n* [[Android (board game)|''Android'' (board game)]], published by Fantasy Flight Gamesn* [[Android (drug)]], brand name for the anabolic steroid methyltestosteronen* [[Android (film)|''Android'' (film)]], directed by Aaron Lipstadtn* [[Android (song)|"Android" (song)]], by The Prodigynn==See also==n* [[The Androids]], Australian rock bandn* [[Droid (disambiguation)]]n* {{Lookfrom}}n* {{Intitle}}nn{{disambiguation}}nn[[cs:Android (rozcestník)]]n[[de:Android]]n[[fa:()]]n[[ko:]]n[[id:Android]]n[[he:]]n[[hu:Android (egyértelmsít lap)]]n[[nl:Android]]n[[ru: ()]]n[[sk:Android (rozli?ovacia stránka)]]n[[sl:Android (razloitev)]]n[[th: ()]]n[[tr:Android]]n[[uk:()]]n[[zh-yue:Android (搞清楚)]]"
               }
             ]
          }
       }
    }
}
此处以考察Android Dome里面的simplewiktionary为例,其核心代码如下:
// 从wiki服务器获取相关页面信息
protected static synchronized String getUrlContent(String url) throws ApiException {
// 检测是否设置了UserAgent属性
/*User Agent中文名为用户代理,简称 UA,
UserAgent是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、
CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等*/
      if (sUserAgent == null) {
                // 抛出自定义异常,提示没有设置UserAgent属性
                throw new ApiException("User-Agent string must be prepared");
      }
      // 新建HttpClient对象
      HttpClient client = new DefaultHttpClient();
      // 新建HttpGet对象
      HttpGet request = new HttpGet(url);
      // 添加用户代理
      request.setHeader("User-Agent", sUserAgent);
      try {
                // 打开连接,获取返回信息
                HttpResponse response = client.execute(request);
                // 检测服务器是否可用
                StatusLine status = response.getStatusLine();
                // 检测服务器返回的状态码
  if (status.getStatusCode() != HTTP_STATUS_OK) {
                        // 如果服务状态码返回不正常,这里就抛出自定义异常
                        throw new ApiException("Invalid response from server: " +
                                   status.toString());
                }
                // 取回服务器返回的内容
                HttpEntity entity = response.getEntity();
                // 将返回的内容保存到InputStream对象中
                InputStream inputStream = entity.getContent();
                // 创建ByteArrayOutputStream对象
                // ByteArrayOutputStream 捕获内存缓存中的数据
                // 然后可以将其转换成字节数组或者字符型 
                ByteArrayOutputStream content = new ByteArrayOutputStream();
                int readBytes = 0;
                // 将缓存中的数据保存到ByteArrayOutputStream对象中
                while ((readBytes = inputStream.read(sBuffer)) != -1) {
                        content.write(sBuffer, 0, readBytes);
                }
                // 将内容转化为字符串
                return new String(content.toByteArray());
      } catch (IOException e) {
                // 检测到IO异常的时候,抛出自定义的异常
                throw new ApiException("API返回错误", e);
      }
}
// 错误检测,自定义异常
public static class ApiException extends Exception {
      // 含有2个参数的构造函数
      public ApiException(String detailMessage, Throwable throwable) {
                super(detailMessage, throwable);
      }
      // 含有1个参数的构造函数
      public ApiException(String detailMessage) {
                super(detailMessage);
      }
}
下面的代码调用了上文定义getUrlContent方法,解析本节开头的那段JSON信息。
/* @param title Wiktionary 页面返回的标题
@param expandTemplates 设置模板是否可用
@返回解析之后的页面内容*/
public static String getPageContent(String title, boolean expandTemplates)
                throws ApiException, ParseException {
      // 如有需要对标题和模板进行编码
      String encodedTitle = Uri.encode(title);
      String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";
      // 查询API获取内容
      String content = getUrlContent(String.format(WIKTIONARY_PAGE,
                encodedTitle, expandClause));
      try {
                // 解析返回的JSON内容
                JSONObject response = new JSONObject(content);
                // 获取query对应的JSON对象
                JSONObject query = response.getJSONObject("query");
                // 获取pages对应的JSON对象
                JSONObject pages = query.getJSONObject("pages");
                // 获取page对应的JSON对象
                JSONObject page = pages.getJSONObject((String) pages.keys().next());
                // 获取revisions对应的JSON数组
                JSONArray revisions = page.getJSONArray("revisions");
                // 获取版本JSON对象,为revisions对应的JSON数组包含的第一个JSON对象
                   JSONObject revision = revisions.getJSONObject(0);
                // 获取revision JSON对象的值
                return revision.getString("*");
      } catch (JSONException e) {
      // 错误处理
      throw new ParseException("API返回错误", e);
      }
}

3.4 Android处理SOAP
3.4.1 SOAP简介
简单对象访问协议(Simple Object Access Protocol,SOAP)是一种标准化的通信规范,主要用于Web服务(Web service)。SOAP的出现可以使网页服务器(Web Server)从XML数据库中提取数据时,无需花时间去格式化页面,并能够让不同应用程序之间通过HTTP协议,以XML格式互相交换彼此的数据,使这个交换过程与编程语言、平台和硬件无关。此标准由IBM、Microsoft、UserLand和DevelopMentor在1998年共同提出,并得到IBM、Lotus(莲花)、Compaq(康柏)等公司的支持,于2000年提交给万维网联盟(World Wide Web Consortium,W3C)。目前SOAP 1.1版是业界共同的标准。
SOAP基于XML标准,用于在分布式环境中发送消息,并执行远程过程调用。使用SOAP,不用考虑任何特定的传输协议(尽管通常选用HTTP协议),就能使数据序列化。
SOAP的优点如下:
SOAP是可扩展的。SOAP无需中断已有的应用程序,SOAP客户端、服务器和协议自身都能发展。而且SOAP能极好地支持中间介质和层次化的体系结构。
SOAP是简单的。客户端发送一个请求,调用相应的对象,然后服务器返回结果。这些消息是XML格式的,并且封装成符合HTTP协议的消息。因此,它符合任何路由器、防火墙或代理服务器的要求。
SOAP是完全和厂商无关的。SOAP可以相对于平台、操作系统、目标模型和编程语言独立实现。另外,传输和语言绑定以及数据编码的参数选择都是由具体的实现决定的。
SOAP与编程语言无关。SOAP可以使用任何语言来完成,只要客户端发送正确SOAP请求(也就是说,传递一个合适的参数给一个实际的远端服务器)。SOAP没有对象模型,应用程序可以捆绑在任何对象模型中。
3.4.2 SOAP消息
1.SOAP消息简介
SOAP使用Internet应用层协议作为其传输协议。SMTP以及HTTP协议都可以用来传输SOAP消息,SOAP亦可以通过HTTPS传输。一条SOAP消息就是一个普通的XML文档,包含下列元素:
必需的Envelope元素,可把此XML文档标识为一条SOAP消息。
可选的Header元素,包含头部信息。
必需的Body元素,包含所有的调用和响应信息。
可选的Fault元素,提供有关在处理此消息时发生错误的信息。
SOAP消息的重要的语法规则如下:
SOAP消息必须使用XML来编码。
SOAP消息必须使用SOAP Envelope命名空间。
SOAP消息必须使用SOAP Encoding命名空间。
SOAP消息不能包含DTD引用。
SOAP消息不能包含XML处理指令。

标签: android

热门推荐