现在的位置: 首页 > 综合 > 正文

android基础知识10:webservice 02:REST

2012年08月07日 ⁄ 综合 ⁄ 共 11040字 ⁄ 字号 评论关闭

          本文主要介绍android客户端如何使用webservice。第一篇介绍ksoap2,第二篇介绍rest。

          android基础知识10:webservice 01:KSOAP2

           android基础知识10:webservice 02:REST

1、REST实例

        最近项目中采用Apache CXF 的REST 方式发布WebService实现,Android手机后台服务的开发,以下以简单是实例实现。

        在项目中采用Android+REST WebService服务方式开发的手机平台很少采用 soap协议这种方式,主要soap协议解析问题,增加了代码量。  采用RESTFull 方式开发WebService的好处,相对SOAP协议的WebService来说,比较简单。同时简化了在手机解析工作,减轻了手机端的压力,提高了手机响应的效率。

        手机后台服务:

package com.easyway.rest.ws;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
 * 服务端发布一个简单的WebService服务
 * 在手机端接受服务端发送的信息.
 *  使用 Apache HttpClient 库访问 JAX-RS web 服务。Jersey 是 JAX-RS 
 *  的参考实现,它简化了 Java™ 环境下的 RESTful Web 服务的开发。Android 
 *  是一款流行的智能手机,本文将展示如何为 Android 创建一个 JAX-RS 客户端。
 *  您将创建一个访问 JAX-RS Web 服务的 Apache HttpClient 库客户端。
 *  JAX-RS必须的jar:
 *  	jersey-bundle-1.8.jar,jersey-server-1.10.jar,jsr311-api-1.1.1.jar
 *      asm-3.1.jar
 *  使用一个 root 资源类创建一个 RESTful Web 服务资源。root 资源类是带有 @PATH 
 *  注释的 POJO。它包含至少一个带注释的方法,该注释为 @PATH、@GET、@PUT、@POST
 *   或 @DELETE。 
 *   
 *   在服务器上,按照 web.xml 的指定,init 参数 com.sun.jersey.config.property.resourceConfigClass 
 *   作为 com.sun.jersey.api.core.PackagesResourceConfig 启动,而 init 参数
 *    com.sun.jersey.config.property.packages 作为 com.easyway.rest.ws 启动。
 *    找到 root 资源类 com.easyway.rest.ws.HelloWorldResource。
 *    
 *  备注:如果采用jersey发布JAXRS服务需要配置:
 *  <pre>
 *   <servlet>
*    <description>JAX-RS</description>
*    <servlet-name>JAX-RS-Servlet</servlet-name>
*    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
*    <init-param>
*        <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
*        <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
*    </init-param>
*    <init-param>
*        <param-name>com.sun.jersey.config.property.packages</param-name>
*        <param-value>com.easyway.rest.ws</param-value>
*    </init-param>
*    <load-on-startup>1</load-on-startup>
*  </servlet>
*  <servlet-mapping>
*    <servlet-name>JAX-RS-Servlet</servlet-name>
*    <url-pattern>/services/*</url-pattern>
*  </servlet-mapping>
 *  
 *  </pre>
 *    
 * @author longgangbai
 *
 */
@Path("/helloworld")
public class HelloWorldResource {
	/**
	 * 一个简单的文本信息
	 * @return
	 */
     @GET
     @Produces(MediaType.TEXT_PLAIN)
     public String getClichedMessage() {
     return "Hello Android";
     }
}
 

web.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>JAXRSWebService</display-name>
<servlet>
    <description>JAX-RS</description>
    <servlet-name>JAX-RS-Servlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
        <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>com.easyway.rest.ws</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JAX-RS-Servlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
</web-app>
 

手机前台服务:

package com.easyway.rest.ws;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
import android.widget.TextView;
/**
 * Android平台主要提供了四种数据存储方式:Shared Preferences、文件存储、Sqlite存储和网络存储。其中:
      1)Shared Preferences 一个轻量级的键-值存储机制,专门用于存储键-值对数据,并且仅可以存储基本的数据类型
                        (boolean、int、long、float和String);通常使用它来存储应用程序的配置信息。
      2)文件存储 通过FileInputStream和FileOutputStream对文件进行操作,在Android中,文件是一个应用程序私有的,
       一个应用程序无法读写其它应用程序的文件。
      3)SQLite存储 SQLite是一款轻型的数据库,支持标准SQL。它的设计目标是嵌入式的,占用资源非常的低,在嵌入式设备中,
          只需要几百K的内存就够了。Android平台也为我们提供了SQLite数据库。
      4)网络存储 以上3种方式数据均存储在手机上,而网络存储的数据是存储在远程服务器上,手机客户端通过联接到网络来存储和获取数据。

      今天要讲解的HttpClient正是常用的网络存储工具之一。记得最早接触HttpClient是在两年前,当时要做一个垂直搜索引擎,
      数据自然是来源于互联网,通过一个爬虫系统不断从指定网站上爬取感兴趣的数据,然后通过Lucene搜索引擎框架实现海量数据
      的快速检索。而爬虫系统最开始是想采用开源的爬虫框架Heritrix来实现,但接触一段时间后发现Heritrix过于庞大,而且是作
      为一个独立的系统运行,不方便嵌入到现有的系统中,再加上学习成本高,最后还是选择了“HttpClient + HtmlParser”来实现的
      小型爬虫系统;其中HttpClient可以模拟HTTP的POST和GET请求,用于从指定网站获取网页数据,而HtmlParser用于解析爬取到
      的页面,过滤HTML标记,取得最终数据。
      是不是发现HttpClient还挺强大的?让我们看看它是什么来头。"HttpClient 是 Apache Jakarta Common 下的子项目,可以用来
      提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议"。如果你以前没
      有接触过HttpClient,那么你只需要简单记住两点就可以了:
          1)HttpClient是一个HTTP协议开发包;
          2)HttpClient不是Android的专利。

      HttpClient的功能介绍:
            1)实现了HTTP请求的所有方法(如GET、POST、PUT、HEAD 等);
            2)支持自动转向;
            3)支持 HTTPS 协议;
            4)支持代理服务器等
      HttpClient的基本使用(以POST请求为例):
            1)创建HttpClient实例(类似于浏览器客户端);
                        HttpClient client = new DefaultHttpClient();
            2)创建HttpPost请求,需要向HttpPost的构造方法传入所请求的URL;
                        HttpPost post = new HttpPost(requestUrl);
            3)发出POST请求(调用HttpClient的execute()方法,execute()的参数为HttpPost实例);
                        HttpResponse response = client.execute(post);
            4)读取返回结果;
            5)释放连接;
            6)对返回的结果进行处理。

      在Android平台上使用HttpClient,并不需要添加额外的jar包,因为Android平台吸收了许多优秀的开源框架,其中就包括HttpClient,
      下面就来看一个Android平台使用HttpClient的例子。
备注:是不是发现HttpClient很容易使用呢?其实,上面所讲解的只是HttpClient最基本的功能(发起POST请求);我们在浏览器客户端
所执行的大多数操作HttpClient都能够模拟,例如:提交表单、查询数据、上传下载文档、页面跳转、Session存储等。比如大家经
常玩“抢车位”、“偷菜”,就可以通过HttpClient编程自动实现。


 * 
 * 
 * 客户端通过Apache HttpClient调用JAXRS WebService的服务。
 * 为 Android 开发访问 JAX-RS Web 服务的 Apache HttpClient 客户端.
 * 
 * 备注:在访问本机的JAXRS Web服务的时候不能使用localhost或者127.0.0.1,
 *    因为android模拟机会调用自身的linux内核操作系统,所以可能找不到相关的服务。
 *    最好填写ip地址如下:
 *       "http://192.168.134.1:8080/JAXRSWebService/services/helloworld";
 *
 * @author longgangbai
 *
 */
public class AndroidJAXRSWebServiceActivity extends Activity {
    private static final String processURL="http://192.168.134.1:8080/JAXRSWebService/services/helloworld";
	private TextView txResult;
	
    /**
     *  Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	///在Android2.2以后必须添加以下代码
		//本应用采用的Android4.0
		//设置线程的策略
		 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()   
         .detectDiskReads()   
         .detectDiskWrites()   
         .detectNetwork()   // or .detectAll() for all detectable problems   
         .penaltyLog()   
         .build());   
		//设置虚拟机的策略
		  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()   
		         .detectLeakedSqlLiteObjects()   
		         //.detectLeakedClosableObjects()   
		         .penaltyLog()   
		         .penaltyDeath()   
		         .build());

        super.onCreate(savedInstanceState);
        //设置UI布局
        setContentView(R.layout.main);
        //获取结果显示文本框
        txResult=(TextView)findViewById(R.id.tvresult);
        //获取JAXRS WebService的结果信息
        getJAXRSWebService();
    }
    
    /**
     * 获取JAXRS WebService的结果信息
     */
    public void getJAXRSWebService(){
    	 try {
    	//创建一个HttpClient对象
    	HttpClient httpclient = new DefaultHttpClient();
        //创建HttpGet对象
    	HttpGet request=new HttpGet(processURL);
    	//请求信息类型MIME每种响应类型的输出(普通文本、html 和 XML)。允许的响应类型应当匹配资源类中生成的 MIME 类型
    	//资源类生成的 MIME 类型应当匹配一种可接受的 MIME 类型。如果生成的 MIME 类型和可接受的 MIME 类型不 匹配,那么将
    	//生成 com.sun.jersey.api.client.UniformInterfaceException。例如,将可接受的 MIME 类型设置为 text/xml,而将
    	//生成的 MIME 类型设置为 application/xml。将生成 UniformInterfaceException。
    	request.addHeader("Accept","text/plain");
        //获取响应的结果
		HttpResponse response =httpclient.execute(request);
		//获取HttpEntity
		HttpEntity entity=response.getEntity();
		//获取响应的结果信息
		String result =EntityUtils.toString(entity);
		 txResult.setText(result);
    	 } catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}

2、REST的理论

        在SOA的基础技术实现方式中WebService占据了很重要的地位,通常我们提到WebService第一想法就是SOAP消息在各种传输协议上交互。近几年REST的思想伴随着SOA逐渐被大家接受,同时各大网站不断开放API提供给开发者,也激起了REST风格WebService的热潮。
       在收到新需求Email之前,我对REST的理解仅仅是通过半懂不懂的看了Fielding的REST博士论文,说实话当时也就是希望了解这么一个新概念,对于其内部的思想只是很肤浅的了解了一下。
       ASF的最新需求就是可能需要实现REST风格的WebService集成,因此不得不好好的去看看REST的真正思想含义以及当前各大网站的设计方式。后面所要表述的也是我这个初学者的一些看法和观点,抛砖引玉,希望在我将REST融入到ASF之前能够获得更多的反馈和意见。
2.1 SOAP
       什么是SOAP,我想不用多说,google一把满眼都是。其实SOAP最早是针对RPC的一种解决方案,简单对象访问协议,很轻量,同时作为应用协议可以基于多种传输协议来传递消息(Http,SMTP等)。但是随着SOAP作为WebService的广泛应用,不断地增加附加的内容,使得现在开发人员觉得SOAP很重,使用门槛很高。在SOAP后续的发展过程中,WS-*一系列协议的制定,增加了SOAP的成熟度,也给SOAP增加了负担。
2.2 REST
       REST其实并不是什么协议也不是什么标准,而是将Http协议的设计初衷作了诠释,在Http协议被广泛利用的今天,越来越多的是将其作为传输协议,而非原先设计者所考虑的应用协议。SOAP类型的WebService就是最好的例子,SOAP消息完全就是将Http协议作为消息承载,以至于对于Http协议中的各种参数(例如编码,错误码等)都置之不顾。其实,最轻量级的应用协议就是Http协议。Http协议所抽象的get,post,put,delete就好比数据库中最基本的增删改查,而互联网上的各种资源就好比数据库中的记录(可能这么比喻不是很好),对于各种资源的操作最后总是能抽象成为这四种基本操作,在定义了定位资源的规则以后,对于资源的操作通过标准的Http协议就可以实现,开发者也会受益于这种轻量级的协议。
       自己理解的将REST的思想归结以下有如下几个关键点:
      1.面向资源的接口设计
所有的接口设计都是针对资源来设计的,也就很类似于我们的面向对象和面向过程的设计区别,只不过现在将网络上的操作实体都作为资源来看待,同时URI的设计也是体现了对于资源的定位设计。后面会提到有一些网站的API设计说是REST设计,其实是RPC-REST的混合体,并非是REST的思想。
       2.抽象操作为基础的CRUD
       这点很简单,Http中的get,put,post,delete分别对应了read,update,create,delete四种操作,如果仅仅是作为对于资源的操作,抽象成为这四种已经足够了,但是对于现在的一些复杂的业务服务接口设计,可能这样的抽象未必能够满足。其实这也在后面的几个网站的API设计中暴露了这样的问题,如果要完全按照REST的思想来设计,那么适用的环境将会有限制,而非放之四海皆准的。      
       3.Http是应用协议而非传输协议
       这点在后面各大网站的API分析中有很明显的体现,其实有些网站已经走到了SOAP的老路上,说是REST的理念设计,其实是作了一套私有的SOAP协议,因此称之为REST风格的自定义SOAP协议。
       4.无状态,自包含
       这点其实不仅仅是对于REST来说的,作为接口设计都需要能够做到这点,也是作为可扩展和高效性的最基本的保证,就算是使用SOAP的WebService也是一样。
2.3 REST vs SOAP
成熟度:

       SOAP虽然发展到现在已经脱离了初衷,但是对于异构环境服务发布和调用,以及厂商的支持都已经达到了较为成熟的情况。不同平台,开发语言之间通过SOAP来交互的web service都能够较好的互通(在部分复杂和特殊的参数和返回对象解析上,协议没有作很细致的规定,导致还是需要作部分修正)
       REST国外很多大网站都发布了自己的开发API,很多都提供了SOAP和REST两种Web Service,根据调查部分网站的REST风格的使用情况要高于SOAP。但是由于REST只是一种基于Http协议实现资源操作的思想,因此各个网站的REST实现都自有一套,在后面会讲诉各个大网站的REST API的风格。也正是因为这种各自实现的情况,在性能和可用性上会大大高于SOAP发布的web service,但统一通用方面远远不及SOAP。由于这些大网站的SP往往专注于此网站的API开发,因此通用性要求不高。
       ASF上考虑发布REST风格的Web Service,可以参考几大网站的设计(兄弟公司的方案就是参考了类似于flickr的设计模式),但是由于没有类似于SOAP的权威性协议作为规范,REST实现的各种协议仅仅只能算是私有协议,当然需要遵循REST的思想,但是这样细节方面有太多没有约束的地方。REST日后的发展所走向规范也会直接影响到这部分的设计是否能够有很好的生命力。
       总的来说SOAP在成熟度上优于REST。
效率和易用性:
       SOAP协议对于消息体和消息头都有定义,同时消息头的可扩展性为各种互联网的标准提供了扩展的基础,WS-*系列就是较为成功的规范。但是也由于SOAP由于各种需求不断扩充其本身协议的内容,导致在SOAP处理方面的性能有所下降。同时在易用性方面以及学习成本上也有所增加。
       REST被人们的重视,其实很大一方面也是因为其高效以及简洁易用的特性。这种高效一方面源于其面向资源接口设计以及操作抽象简化了开发者的不良设计,同时也最大限度的利用了Http最初的应用协议设计理念。同时,在我看来REST还有一个很吸引开发者的就是能够很好的融合当前Web2.0的很多前端技术来提高开发效率。例如很多大型网站开放的REST风格的API都会有多种返回形式,除了传统的xml作为数据承载,还有(JSON,RSS,ATOM)等形式,这对很多网站前端开发人员来说就能够很好的mashup各种资源信息。
       因此在效率和易用性上来说,REST更胜一筹。
安全性:
       这点其实可以放入到成熟度中,不过在当前的互联网应用和平台开发设计过程中,安全已经被提到了很高的高度,特别是作为外部接口给第三方调用,安全性可能会高过业务逻辑本身。
       SOAP在安全方面是通过使用XML-Security和XML-Signature两个规范组成了WS-Security来实现安全控制的,当前已经得到了各个厂商的支持,.net ,php ,java 都已经对其有了很好的支持(虽然在一些细节上还是有不兼容的问题,但是互通基本上是可以的)。
       REST没有任何规范对于安全方面作说明,同时现在开放REST风格API的网站主要分成两种,一种是自定义了安全信息封装在消息中(其实这和SOAP没有什么区别),另外一种就是靠硬件SSL来保障,但是这只能够保证点到点的安全,如果是需要多点传输的话SSL就无能为力了。安全这块其实也是一个很大的问题,今年在BEA峰会上看到有演示采用SAML2实现的网站间SSO,其实是直接采用了XML-Security和XML-Signature,效率看起来也不是很高。未来REST规范化和通用化过程中的安全是否也会采用这两种规范,是未知的,但是加入的越多,REST失去它高效性的优势越多。
2.4 应用设计与改造:
       我们的系统要么就是已经有了那些需要被发布出去的服务,要么就是刚刚设计好的服务,但是开发人员的传统设计思想让REST的形式被接受还需要一点时间。同时在资源型数据服务接口设计上来说按照REST的思想来设计相对来说要容易一些,而对于一些复杂的服务接口来说,可能强要去按照REST的风格来设计会有些牵强。这一点其实可以看看各大网站的接口就可以知道,很多网站还要传入function的名称作为参数,这就明显已经违背了REST本身的设计思路。
       而SOAP本身就是面向RPC来设计的,开发人员十分容易接受,所以不存在什么适应的过程。
总的来说,其实还是一个老观念,适合的才是最好的
       技术没有好坏,只有是不是合适,一种好的技术和思想被误用了,那么就会得到反效果。REST和SOAP各自都有自己的优点,同时如果在一些场景下如果去改造REST,其实就会走向SOAP(例如安全)。
       REST对于资源型服务接口来说很合适,同时特别适合对于效率要求很高,但是对于安全要求不高的场景。而SOAP的成熟性可以给需要提供给多开发语言的,对于安全性要求较高的接口设计带来便利。所以我觉得纯粹说什么设计模式将会占据主导地位没有什么意义,关键还是看应用场景。
       同时很重要一点就是不要扭曲了REST现在很多网站都跟风去开发REST风格的接口,其实都是在学其形,不知其心,最后弄得不伦不类,性能上不去,安全又保证不了,徒有一个看似象摸象样的皮囊。

参考文献:

Android+REST WebService服务方式手机开发

SOAPvsREST_WebService

【上篇】
【下篇】

抱歉!评论已关闭.