Spring For Android初步

徐正雅
2023-12-01

Spring For Android初步

Spring for Android 支持在Android环境下使用Spring框架,其意在简化Android本地开发。这包括能够在你的Android应用程序的Rest客户端使用RestTemplate。除了本次主要讲解的网络请求部分,Spring for Android还提供了基于OAuth支持将Spring Social的功能集成到Android应用程序, 授权客户机和实现流行的社交网站,如Twitter和Facebook。

RestTemplate Module

Spring的RestTemplate是一个健壮的、流行的基于java的REST client。Spring Android的RestTemplate模块提供一个能够在Android环境下工作的RestTemplate版本。

RestTemplate类是SFA RestTemplate组件包的核心类。他在概念上类似Spring框架其他同级项目中提供的模板类。RestTemlpate的行为表现取决于他提供的那些回调方法,同时通过配置合适的HttpMessageConverter 类,用户可以高效的将对象转换成HTTP请求中的信息或从响应消息中将信息转换回对应的对象。

    每当创建一个新的RestTemplate实例,构造器就会生成配套的辅助对象用于支持RestTemplate的功能。以下是RestTemplate组件中一些提供支持的模块介绍。

HTTP Client

    RestTemplate 提供了抽象的RESTful HTTP 请求的模块,内部实现中,RestTemplate 封装了原生的Android HTTP 客户端连接包。这包括标准的J2SE连接组件(用SimpleClientHttpRequestFactory封装)和HttpComponents HttpClient组件(用HttpComponentsClientHttpRequestFactory封装)。默认情况下具体使用哪种ClientHttpRequestFactory 取决于Android的版本。

@SuppressWarnings("deprecation")
protected HttpAccessor() {
   if (httpClient43Present) {
      this.requestFactory = new HttpComponentsClientHttpRequestFactory();
   }
   else if (okHttpPresent) {
      this.requestFactory = new OkHttpClientHttpRequestFactory();
   }
   else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
      this.requestFactory = new SimpleClientHttpRequestFactory();
   }
   else {
      this.requestFactory = new org.springframework.http.client.HttpComponentsAndroidClientHttpRequestFactory();
   }
}

(2.3之后建议使用标准的J2SE连接组件

 

Gzip压缩器

HTTP规范中允许通过在请求头中加入Accept-Encoding参数指定传输字节流的压缩方式。目前为止,RestTemplate 可以通过Gzip压缩组件支持在发送和接受时通过gzip(仅支持gzip)格式压缩过的数据。

对象-Json转换

如果需要支持这种转换,SFA的RestTemplate需要第三方Json数据映射包的支持。目前SFA提供三种包支持:Jackson JSON ProcessorJackson 2.x, 和 Google Gson。虽然Jackson 系列是有名的JSON解析包,但Gson的包更小,更适合生成小型的Android程序安装包。
    SFA 将支持代码放在包 org.springframework.http.converter.json中。

RSS和Atom 摘要文件支持

    RSS和Atom的支持也需要第三方包, Android ROME Feed Reader包就提供了相应的功能。

SFA 将支持代码放在包org.springframework.http.converter.feed 中。

所需要的包文件

  • spring-android-rest-template-{version}.jar

  • spring-android-core-{version}.jar

 

RestTemplate 构造器

    下面列出四种RestTemplate 的构造方法。默认的构造方法不包含消息的转换器,因此必须自己添加消息转换器。

    如上文所讲,如果需要一个特别ClientHttpRequestFactory ,也可以向构造器中传入一个ClientHttpRequestFactory 的实例参数。

  • RestTemplate();

  • RestTemplate(boolean includeDefaultConverters);

  • RestTemplate(ClientHttpRequestFactory requestFactory);

  • RestTemplate(boolean includeDefaultConverters, ClientHttpRequestFactory requestFactory);

 

RestTemplate API简介

    RestTemplate 对应主要的六种Http 访问方法封装了6类高层调用方法,通过他们可以非常轻松且高效的完成对RESTful 服务的服务请求。

    RestTemplate 的API方法命名遵循同样的规则:  method+return

比如getForObject() 将调用HTTP GET方法,同时将HTTP响应数据转换成你指定的对象类型并返回。而postForLocation() 将调用HTTP POST方法,将给定的对象转换成HTTP请求内容发送,返回的信息中将标识新的对象资源所在的URL。

  当HTTP请求发生异常时,异常信息将被封装在RestClientException 中并被抛出,可以通过在RestTemplate中实现ResponseErrorHandler来捕获并处理。

restTemplate.setErrorHandler(errorHandler);

/*捕获http请求发生异常RestClientException*/

restTemplate.setErrorHandler(new ResponseErrorHandler() {

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        //获得请求响应,通过判断执行不同的操作。。。
        return false;//返回true表示有错误,false表示没有错误
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        //处理相应的错误操作。。。
        //该方法只有在hasError方法中返回true时才会执行
    }
});

网络请求方法(其他请求方式类似,可参考官方在线的API文档,链接在参考资料中):

HTTP GET

public <T> T getForObject(String url, Class<T> responseType, Object… urlVariables) throws RestClientException;

public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException;

public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException;

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object… urlVariables);

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables);

public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;

 

HTTP POST

public URI postForLocation(String url, Object request, Object… urlVariables) throws RestClientException;

public URI postForLocation(String url, Object request, Map<String, ?> urlVariables);

public URI postForLocation(URI url, Object request) throws RestClientException;

public <T> T postForObject(String url, Object request, Class<T> responseType, Object… uriVariables);

public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);

public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException;

public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object… uriVariables);

public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;

HTTP 消息转换器

对象通过getForObject(), getForEntity(), postForLocation(), postForEntity(), postForObject() and put() 发送或者返回消息时都是通过HttpMessageConverter 的具体实现,从而转换成Http请求或者从Http应答中转换。从下面的HttpMessageConverter  API我们能对他的功能有个更深的认识。 

public interface HttpMessageConverter<T> {
//判断给定的类型是否可以被转换器读取
//clazz 用于测试是否能够读取
//mediaType 读取的网络媒介类型,如果不指定,可以为null
//如果返回true表示可读,false表示其他情况
boolean canRead(Class<?> clazz, MediaType mediaType);
// 判断给定的类型是否可以被转换器写入  
//clazz 用于测试是否能够写入
//mediaType 写入的网络媒介类型,如果不指定,可以为null
//如果返回true表示可写,false表示其他情况
boolean canWrite(Class<?> clazz, MediaType mediaType);
// 返回可被该转换器支持的header头消息类型   
List<MediaType> getSupportedMediaTypes();

// 将给定的inputMessage消息转换成相应的类型对象并返回  
// clazz 返回的类型,这个类型必须预先通过canRead方法,返回true
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
    throws IOException, HttpMessageNotReadableException;

    // 将给定的对象转换后写入outputMessage  
    // t 写入outputMessage的对象,这个对象的类型必须预先通过canWrite方法,并返回true
    // contentType  写入时的媒介类型,如果为null的话,则必须使用默认的内容类型转换器,否则,媒介类型必须预先通过canWrite方法且返回值为true
    // outputMessage  要写入的消息
   void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;
}

其中,SFA框架已经提供了现成的MediaType(即mime类型)。

部分类型:

{ ".323", "text/h323" },

{ ".3gp", "video/3gpp" },

{ ".aab", "application/x-authoware-bin" },

{ ".aam", "application/x-authoware-map" },

{ ".aas", "application/x-authoware-seg" },

{ ".acx", "application/internet-property-stream" },

{ ".ai", "application/postscript" },

{ ".aif", "audio/x-aiff" },

{ ".aifc", "audio/x-aiff" },

{ ".aiff", "audio/x-aiff" },

{ ".als", "audio/X-Alpha5" },

{ ".amc", "application/x-mpeg" },

{ ".ani", "application/octet-stream" },

{ ".apk", "application/vnd.android.package-archive" },

{ ".asc", "text/plain" }

默认的消息转换器

基于性能的选择考虑,默认的RestTemplate无参构造方法不包含任何消息转换器。但是,如果调用了布尔版本的构造方法并传递true,则构造器会为一些主要的mime类型增加转换器。当然自己也可以编写自定义的转换器,并通过messageConverters属性添加。
    RestTemplate生成的转换器包括:ByteArrayHttpMessageConverter, StringHttpMessageConverter, 和ResourceHttpMessageConverter。如果Android的版本在2.2或以上,那么XmlAwareFormHttpMessageConverter 和SourceHttpMessageConverter也会被添加。否则,若在2,.1版本,因为缺少对XML的必要支持,这两个转换器会被FormHttpMessageConverter 代替。我们来看看代码中是如何初始化的。

    如果不设置,则使用该默认的消息转换器:

/**
 * Identifies and initializes default {@link HttpMessageConverter} implementations.
 */

private static class DefaultMessageConverters {
   private static final boolean javaxXmlTransformPresent = 
         ClassUtils.isPresent("javax.xml.transform.Source", RestTemplate.class.getClassLoader());

   private static final boolean simpleXmlPresent =
         ClassUtils.isPresent("org.simpleframework.xml.Serializer", RestTemplate.class.getClassLoader());

   private static final boolean jackson2Present =
         ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) &&
         ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader());

   private static final boolean gsonPresent =
         ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader());

   public static void init(List<HttpMessageConverter<?>> messageConverters) {
      messageConverters.add(new ByteArrayHttpMessageConverter());
      messageConverters.add(new StringHttpMessageConverter());
      messageConverters.add(new ResourceHttpMessageConverter());

      // if javax.xml.transform is not available, fall back to standard Form message converter
      if (javaxXmlTransformPresent) {
         messageConverters.add(new SourceHttpMessageConverter<Source>());
         messageConverters.add(new AllEncompassingFormHttpMessageConverter());
      }
      else {
         messageConverters.add(new FormHttpMessageConverter());
      }
      if (simpleXmlPresent) {
         messageConverters.add(new SimpleXmlHttpMessageConverter());
      }
      if (jackson2Present) {
         messageConverters.add(new MappingJackson2HttpMessageConverter());
      } 
      else if (gsonPresent) {
         messageConverters.add(new GsonHttpMessageConverter());
      }
   }
}

ByteArrayHttpMessageConverter

     该转换器可以从HTTP请求或应答中读写字节数组,默认情况下,该转换器支持所有的media types (*/*),同时写数据会以Content-Type为application/octet-stream(任意二进制数据)的形式。

FormHttpMessageConverter

能处理格式化的数据。支持用application/x-www-form-urlencoded(格式化编码数据)和multipart/form-data(多组件格式数据)的读写转换,格式化的数据通常用一个MultiValueMap<String, String>数据结构来读写。

XmlAwareFormHttpMessageConverter

是FormHttpMessageConverter的扩展,通过SourceHttpMessageConverter增加了对XML转换的支持。

ResourceHttpMessageConverter

能处理资源。支持对所有类型的读操作,写资源支持application/octet-stream。

SourceHttpMessageConverter

可以将请求或应答数据转换成javax.xml.transform.Source类型,只支持DOMSource, SAXSource, 和StreamSource三种类型。默认情况下支持text/xml和 application/xml两种XML可扩展标记语言MIME类型。

StringHttpMessageConverter

可以将请求或应答数据转换成String类型,支持所有的text media类型(text/*),支持Content-Type为text/plain(原文数据)写数据。

SimpleXmlHttpMessageConverter

支持对XML格式的读写处理,需要第三方包Simple XML serializer。XML的映射工作可以通过该包提供的注解方式进行配置。如果有需要提供附加的控制功能,可以通过切面的形式通过一个可定制的Serializer进行功能的加强。默认情况下,支持读写application/xml, text/xml, 和application/*+xml这几种MIME类型。

    需要注意的是,此框架和Spring OXM并不兼容。他是用在SFA中的独立的第三方XML串行化实现。基于maven的依赖如下

<dependency>
    <groupId>org.simpleframework</groupId>
    <artifactId>simple-xml</artifactId>
    <version>${simple-version}</version>
</dependency>  


MappingJackson2HttpMessageConverter
    基于Jackson框架提供对请求与应答数据的Json-Obejct转换。对于Json的映射配置可以Jackson提供的注解支持。当串行化/反串行化需要时也支持切面式的控制管理,默认支持application/json类型的读写。
    请注意该转换器和GsonHttpMessageConverter 转换器都支持application/json类型的数据转换,因此最好只添加一款转换器,因为RestTemplate 会优先选择他最早匹配到的转换器进行数据转换,所以两款转换器都添加会导致不可预料的结果。

    MappingJackson2HttpMessageConverter如果不是通过maven而是手工添加,还需要在lib包中引入jackson-annotations和jackson-core jars。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson-version}</version>
</dependency>  


    也可以通过MappingJacksonHttpMessageConverter支持 Jackson JSON Processor包。同样,手工导入需要引入jackson-core-asl jar。

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>${jackson-version}</version>
</dependency>  

GsonHttpMessageConverter
    基本上和MappingJackson2HttpMessageConverter的功能一直,也支持注解和切面式。

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>${gson-version}</version>
</dependency>  

(以上也可以使用gradle插件进行配置)

使用gzip压缩

// 添加 Accept-Encoding 消息头属性,设置为gzip
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAcceptEncoding(ContentCodingType.GZIP);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// 注意调用的方法
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);

Gzip压缩能有效的减少通讯传输的信息量。Gzip格式传输必须在服务器端被支持。通过将内容的请求头Accept-Encoding设置为gzip,可以向服务器端请求使用gzip压缩传输。如果服务器端支持,将返回压缩后的数据。RestTemplate 将检查Content-Encoding属性判断应答返回数据是否是压缩过的,是则用GZIPInputStream 去解压缩。目前Content-Encoding仅支持gzip格式。
需要注意,如果在2.3或以后的版本使用基于J2SE的标准连接SimpleClientHttpRequestFactory,Android框架会自动在Accept-Encoding中设置gzip,如果希望是gzip压缩失效,必须在header信息中增加其他标志值:
HttpHeaders requestHeaders = new HttpHeaders();
//增加如下标识
requestHeaders.setAcceptEncoding(ContentCodingType.IDENTITY);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);  

通过GET方式获取JSON数据

    首先需要定义一个与返回的JSON格式对应的POJO类。

public class Event {
    private Long id;
    private String title;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
       return title;
    }
    public String setTitle(String title) {
       this.title = title;
    }
}  

使用Rest请求:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Event[] events = restTemplate.getForObject(url, Event[].class);

也可以通过设置请求头Accept信息的方式
// 显式设置 Accept header
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType("application","json")));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
ResponseEntity<Event[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Event[].class);
Event[] events = responseEntity.getBody(); 
 

其中MappingJackson2HttpMessageConverter可以换成GsonHttpMessageConverter,作为另一种实现JSON解析的选择。

 

通过GET方式获取 XML 数据

    我们使用刚刚用过的Event对象来演示XML的使用方法,注意Event的类及属性都有注解。

@Root
public class Event {
    @Element
    private Long id;
    @Element
    private String title;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public String setTitle(String title) {
        this.title = title;
    }
}  

    如果需要解析一组Event的数据,我们还需要定义基于List的包装类。

@Root(name="events")
public class EventList {
   @ElementList(inline=true)
   private List<Event> events;
   public List<Event> getEvents() {
      return events;
   }
   public void setEvents(List<Event> events) {
       this.events = events;
   }
}  

    编写请求代码如下:

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new SimpleXmlHttpMessageConverter());
EventList eventList = restTemplate.getForObject(url, EventList.class);  

    和刚才一样,也可以通过设置请求头Accept信息的方式:

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Collections.singletonList(new MediaType("application","xml")));
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new SimpleXmlHttpMessageConverter());
ResponseEntity<EventList> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, EventList.class);
EventList eventList = responseEntity.getBody();  

通过POST的方式发送 JSON 数据

    我们定义了如下的POJO用于演示:

public class Message {
    private long id;
    private String subject;
    private String text;
    public void setId(long id) {
        this.id = id;
    }
    public long getId() {
        return id;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getSubject() {
        return subject;
    }
    public void setText(String text) {
        this.text = text;
    }
    public String getText() {
        return text;
    }
}  

    接着是编辑数据和发送请求的代码

// 编辑数据  
Message message = new Message();
message.setId(555);
message.setSubject("test subject");
message.setText("test text");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
String response = restTemplate.postForObject(url, message, String.class);  

同样,通过设置请求头Content-Type信息也是可以的

// 编辑数据
Message message = new Message();
message.setId(555);
message.setSubject("test subject");
message.setText("test text");
// 设置 Content-Type
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(new MediaType("application","json"));
HttpEntity<Message> requestEntity = new HttpEntity<Message>(message, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
String result = responseEntity.getBody();  

(注意:如果想用spring for android的json解析功能,服务器需要返回的type类型应该为application/json而不是通常的text/html)

其他: Spring for Android还提供了三方授权,本身提供facebook和twtter的三方授,需要第三方包的支持。提供了Spring Social组件用于创建自己的连接工厂,并提供加密和保存信息的功能。

Spring for Android的核心部分比较强调服务器和客户端之间的数据交流,服务器需要确实返回指定的MIME类型,客户端才能进行更加简便的数据解析。

 

参考资料:

http://projects.spring.io/spring-android/

http://docs.spring.io/spring-android/docs/1.0.x/reference/htmlsingle/

http://tool.oschina.net/apidocs/apidoc?api=springforandroid

http://blog.csdn.net/yoara/article/details/37963497

http://blog.csdn.net/icyfox_bupt/article/details/9572813

http://www.tuicool.com/articles/qu6v2eJ

 类似资料: