原文地址:点击打开链接
1.Spring for Android(SFA) 概述
1.1 前言
对于熟悉使用Spring框架的开发人员,SFA为他们在Android的环境下开发提供了有力的支持。通过内嵌的RestTemplate组件,SFA项目可为您的项目提供便利的REST客户端开发方式,这种用法很像spring提供的jdbc模板类,能有效的简化客户端的开发工作量。同时,SFA也将一系列健壮的、基于开放授权的Spring社交功能集成到您的应用中,这将为您提供实现流行的社交网站类似Twitter、非死不可等的授权连接提供便利之法。
2.RestTemplate 组件
2.1 前言
2.2 概述
2.2.1 HTTP Client
protected HttpAccessor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
this.requestFactory = new SimpleClientHttpRequestFactory();
} else {
this.requestFactory = new HttpComponentsClientHttpRequestFactory();
}
}
Google建议在2.3或以后版本中使用J2SE标准http client组件,而之前最好选择HttpComponents HttpClient。当然,您也可以通过向RestTemplate构造其中传入ClientHttpRequestFactory的实例或者通过setRequestFactory(ClientHttpRequestFactory requestFactory) 去选择您希望的连接组件的具体实现者。
2.2.2 Gzip压缩器
HTTP规范中允许通过在请求头中加入Accept-Encoding参数指定传输字节流的压缩方式。目前为止,RestTemplate 可以通过Gzip压缩组件支持在发送和接受时通过gzip(仅支持gzip)格式压缩过的数据。2.2.3 对象-Json转换
SFA 将支持代码放在包 org.springframework.http.converter.json中。
2.2.5 RSS和Atom 摘要文件支持
2.3 所需要的包文件
- spring-android-rest-template-{version}.jar
- spring-android-core-{version}.jar
2.4 RestTemplate 构造器
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
通过带boolean参数的构造器可以生成具有默认消息转换器的RestTemplate 实例,关于转换器的更多细节,我们在下面讲解。
- RestTemplate();
- RestTemplate(boolean includeDefaultConverters);
- RestTemplate(ClientHttpRequestFactory requestFactory);
- RestTemplate(boolean includeDefaultConverters, ClientHttpRequestFactory requestFactory);
2.5 RestTemplate API简介
methodReturn
当HTTP请求发生异常时,异常信息将被封装在RestClientException 中并被抛出,可以通过在RestTemplate中实现ResponseErrorHandler来捕获并处理。
restTemplate.setErrorHandler(errorHandler);
RestTemplate.protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response)) {
logResponseStatus(method, url, response);
}
else { //如果有异常,调用相应的handler处理
handleResponseError(method, url, response);
}
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
else {
return null;
}
}
catch (IOException ex) {
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
2.5.1 HTTP DELETE
- public void delete(String url, Object... urlVariables) throws RestClientException;
- public void delete(String url, Map<String, ?> urlVariables) throws RestClientException;
- public void delete(URI url) throws RestClientException;
2.5.2 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;
2.5.3 HTTP HEAD
- public HttpHeaders headForHeaders(String url, Object... urlVariables) throws RestClientException;
- public HttpHeaders headForHeaders(String url, Map<String, ?> urlVariables) throws RestClientException;
- public HttpHeaders headForHeaders(URI url) throws RestClientException;
2.5.4 HTTP OPTIONS
- public Set<HttpMethod> optionsForAllow(String url, Object... urlVariables) throws RestClientException;
- public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> urlVariables) throws RestClientException;
- public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException;
2.5.5 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;
2.5.6 HTTP PUT
- public void put(String url, Object request, Object... urlVariables) throws RestClientException;
- public void put(String url, Object request, Map<String, ?> urlVariables) throws RestClientException;
- public void put(String url, Object request, Map<String, ?> urlVariables) throws RestClientException;
2.6 HTTP 消息转换器
public interface HttpMessageConverter<T> {
// 判断给定的类型是否可以被转换器读取
boolean canRead(Class<?> clazz, MediaType mediaType);
// 判断给定的类型是否可以被转换器写入
boolean canWrite(Class<?> clazz, MediaType mediaType);
// 返回可被该转换器支持的header头消息类型
List<MediaType> getSupportedMediaTypes();
// 将给定的inputMessage消息转换成相应的类型对象并返回
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// 将给定的对象转换后写入outputMessage
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
org.springframework.http.MediaType
2.6.1 默认的消息转换器
基于性能的选择考虑,默认的RestTemplate无参构造方法不包含任何消息转换器。但是,如果你调用了布尔版本的构造方法并传递true,则构造器会为一些主要的mime类型增加转换器。当然您自己也可以编写自定义的转换器,并通过messageConverters属性添加。RestTemplate生成的转换器包括:ByteArrayHttpMessageConverter, StringHttpMessageConverter, 和ResourceHttpMessageConverter。如果Android的版本在2.2或以上,那么XmlAwareFormHttpMessageConverter 和SourceHttpMessageConverter也会被添加。否则,若在2,.1版本,因为缺少对XML的必要支持,这两个转换器会被FormHttpMessageConverter 代替。我们来看看代码中是如何初始化的。
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 jacksonPresent =
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", RestTemplate.class.getClassLoader()) &&
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", 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 romePresent =
ClassUtils.isPresent("com.google.code.rome.android.repackaged.com.sun.syndication.feed.synd.SyndFeed", RestTemplate.class.getClassLoader());
public static void init(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
// 如果 javax.xml.transform 不能被类加载器找到,则退回到FormHttpMessageConverter版本
if (javaxXmlTransformPresent) {
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new XmlAwareFormHttpMessageConverter());
} else {
messageConverters.add(new FormHttpMessageConverter());
}
if (simpleXmlPresent) {
messageConverters.add(new SimpleXmlHttpMessageConverter());
}
if (jackson2Present) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
} else if (jacksonPresent) {
messageConverters.add(new MappingJacksonHttpMessageConverter());
}
if (romePresent) {
messageConverters.add(new SyndFeedHttpMessageConverter());
}
}
}
2.6.2 关于MIME的介绍
2.6.3 SFA自带的消息转换器
现在我们来看看SFA框架中的HttpMessageConverter实现类。对于任何一种转换器都已经对应了默认的media类型(mime),但是可以通过supportedMediaTypes属性覆盖。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>
SyndFeedHttpMessageConverter
基于 Android ROME Feed Reader包用于读写 RSS和Atom feeds的转换器。数据将被转换成com.google.code.rome.android.repackaged和com.sun.syndication.feed.synd.SyndFeed类型。默认情况下支持application/rss+xml 和application/atom+xml两种MIME类型。SyndFeedHttpMessageConverter是RssChannelHttpMessageConverter和AtomFeedHttpMessageConverter的集合体,如果您只需要其中之一,您可以使用任意一个转换器替换SyndFeedHttpMessageConverter。
在Android2.1或更早,该包还依赖于JDOM包。
<dependency>
<groupId>com.google.code.android-rome-feed-reader</groupId>
<artifactId>android-rome-feed-reader</artifactId>
<version>${android-rome-version}</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>${jdom-fork-version}</version>
</dependency>
Android ROME Feed Reader
包还没有通过Maven的中央仓库管理,因此您需要添加下面的远程仓库到您的POM配置文件中。
<!-- For developing with Android ROME Feed Reader -->
<repository>
<id>android-rome-feed-reader-repository</id>
<name>Android ROME Feed Reader Repository</name>
<url>https://android-rome-feed-reader.googlecode.com/svn/maven2/releases</url>
</repository>
2.7 一些简单的例子
2.7.1 Basic Usage Example
下面的代码展示了最基本的用法String url = "http://www.baidu.com";
// 创建一个RestTemplate实例
RestTemplate restTemplate = new RestTemplate();
// 添加String转换器
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// 发送 HTTP GET 请求, 并将应答消息转换成 String类型数据
String result = restTemplate.getForObject(url, String.class, "SpringSource");
返回形如HTML
2.7.2 Using Gzip Compression
Gzip压缩能有效的减少通讯传输的信息量。Gzip格式传输必须在服务器端被支持。通过将内容的请求头in西Accept-Encoding甚至为gzip,您可以向服务器端请求使用gzip压缩传输。如果服务器端支持,他将返回压缩后的数据。RestTemplate 将检查Content-Encoding属性判断应答返回数据是否是压缩过的,是则用GZIPInputStream 去解压缩。目前Content-Encoding仅支持gzip 格式。以下代码展示了如何告知服务器端用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);
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);
2.7.3 通过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解析的选择。
2.7.4 通过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();
2.7.5 通过POST的方式发送 JSON 数据
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();
2.7.6 获取 RSS 或 Atom 的订阅信息
下面是一个加载RSS订阅信息的例子:RestTemplate restTemplate = new RestTemplate();
//
restTemplate.getMessageConverters().add(new SyndFeedHttpMessageConverter());
// Make the HTTP GET request, marshaling the response to a SyndFeed object
SyndFeed feed = restTemplate.getForObject(url, SyndFeed.class);
SyndFeedHttpMessageConverter syndFeedConverter = new SyndFeedHttpMessageConverter();
//设置 指定的MIME类型
syndFeedConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_XML));
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(syndFeedConverter);
//
SyndFeed feed = restTemplate.getForObject(url, SyndFeed.class);
2.7.7 HTTP 基本的权限验证
下面的例子演示如何向HTTP请求头中填充用户名密码等验证信息。如果验证通过将返回请求的相应,否则服务端将返回401验证不通过。RestTemplate可以通过抛出HttpClientErrorException来处理验证不通过相应信息,通过exception的getStatusCode()您可以确定具体的原因及确定如何处理他。HttpAuthentication authHeader = new HttpBasicAuthentication(username, password);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
try {
//
ResponseEntity<Message> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
return response.getBody();
} catch (HttpClientErrorException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
// 处理401 Unauthorized
}
3. Auth Module
3.1 Introduction
3.2 Overview
3.2.1 SQLite Connection Repository
3.2.2 Encryption
3.3 所需要的包
- spring-android-auth-{version}.jar
- spring-android-rest-template-{version}.jar
- spring-android-core-{version}.jar
- spring-security-crypto-{version}.jar
- spring-social-core-{version}.jar
- spring-social-twitter-{version}.jar
- spring-social-core-{version}.jar
- spring-security-crypto-{version}.jar
- jackson-mapper-asl-{version}.jar
- jackson-core-asl-{version}.jar
3.4 使用案例
3.4.1 Initializing the SQLite Database
SQLiteConnectionRepositoryHelper 继承自SQLiteOpenHelper。为了避免内存泄露等问题,您最好通过传递Application Context类型的上下文引用给SQLiteConnectionRepositoryHelper构造器用于生成新的实例。当应用第一次使用SQLiteConnectionRepositoryHelper 时,一个名为spring_social_connection_repository.sqlite的数据库文件将被生成用于存储连接信息。Context context = getApplicationContext();
SQLiteOpenHelper repositoryHelper = new SQLiteConnectionRepositoryHelper(context);
3.4.2 Single User App Environment
这个例子将演示如何为多连接建立一个连接库。为了建立链接库,您需要以下对象:- ConnectionFactoryRegistry connectionFactoryRegistry;
- SQLiteOpenHelper repositoryHelper;
- ConnectionRepository connectionRepository;
ConnectionFactoryRegistry
对象为应用存储了不同的连接信息。
connectionFactoryRegistry = new ConnectionFactoryRegistry();
// 加密的ID和密钥
String consumerToken = "YR571S2JiVBOFyJS5MEg";
String consumerTokenSecret = "Kb8hS0luftwCJX3qVoyiLUMfZDtK1EozFoUkjNLUMx4";
// 提供Twitter的连接工厂
TwitterConnectionFactory twitterConnectionFactory;
twitterConnectionFactory = new TwitterConnectionFactory(consumerToken, consumerTokenSecret)
connectionFactoryRegistry.addConnectionFactory(facebookConnectionFactory);
最后一步就是为存储不同的服务供应商的连接信息准备连接库。
// 用上下文创建SQLiteOpenHelper对象
Context context = getApplicationContext();
SQLiteOpenHelper repositoryHelper = new SQLiteConnectionRepositoryHelper(context);
// 连接库用 TextEncryptor 对象加密OAuth信息
TextEncryptor textEncryptor = AndroidEncryptors.noOpText();
// 创建 connection repository
ConnectionRepository connectionRepository = new SQLiteConnectionRepository(repositoryHelper,
connectionFactoryRegistry, textEncryptor);
3.4.3 加密 OAuth 数据
通过TextEncryptor类Spring Social组件支持加密用户的OAuth连接信息然后保存进ConnectionRepository 中。password和salt(佐料,乱序值)可用于生成加密后的密钥。salt值必须是16进制的、随机的和应用中保持一致且通用的。String password = "password";
String salt = "5c0744940b5c369b";
TextEncryptor textEncryptor = AndroidEncryptors.text(password, salt);
connectionRepository = new SQLiteConnectionRepository(repositoryHelper,
connectionFactoryRegistry, textEncryptor);
TextEncryptor textEncryptor = AndroidEncryptors.noOpText();
connectionRepository = new SQLiteConnectionRepository(repositoryHelper,
connectionFactoryRegistry, textEncryptor);
3.4.4 创建一个 基于OAuth 1.0a协议的连接
TwitterConnectionFactory connectionFactory;
connectionFactory = (TwitterConnectionFactory) connectionFactoryRegistry.getConnectionFactory(Twitter.class);
第二步,生成一次性的请求令牌,用于后续的步骤。
OAuth1Operations oauth = connectionFactory.getOAuthOperations();
// 回调url用于应用程序响应OAuth校验
String callbackUrl = "x-org-springsource-android-showcase://twitter-oauth-response";
// 从Twitter中获取一次性的请求令牌
OAuthToken requestToken = oauth.fetchRequestToken(callbackUrl, null);
String authorizeUrl = oauth.buildAuthorizeUrl(requestToken.getValue(), OAuth1Parameters.NONE);
<activity android:name="org.springframework.android.showcase.social.twitter.TwitterActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="x-org-springsource-android-showcase" android:host="twitter-oauth-response" />
</intent-filter>
</activity>
响应回调的Activity必须要用"oauth_verifier "键从请求中获得OAuth校验。
Uri uri = getIntent().getData();
String oauthVerifier = uri.getQueryParameter("oauth_verifier");
一旦你拥有了OAuth检验,你就可以验证刚刚生成的请求令牌了。
AuthorizedRequestToken authorizedRequestToken = new AuthorizedRequestToken(requestToken, verifier);
OAuth1Operations oauth = connectionFactory.getOAuthOperations();
OAuthToken accessToken = oauth.exchangeForAccessToken(authorizedRequestToken, null);
最后一步,我们创建Twitter连接并把它保存到连接库中。
Connection<TwitterApi> connection = connectionFactory.createConnection(accessToken);
connectionRepository.addConnection(connection);
3.4.5 Establishing an OAuth 2.0 connection
接下来基于OAuth2.0协议的方式创建非死不可的连接。请注意不同的服务供应商实现是不同的,您需要针对OAuth2.0的供应商最适当的调整。第一步获取factory,这一部和上面的一样,注意先前要先生成factory。
FacebookConnectionFactory connectionFactory;
connectionFactory = (FacebookConnectionFactory) connectionFactoryRegistry.getConnectionFactory(Facebook.class);
指定重定向URL,在非死不可的情景里,我们需要使用客户端认证流程。为了获取一个访问令牌,非死不可会重定向到一个在URI的信息段中包含访问令牌的成功页面上。
String redirectUri = "https://www.facebook.com/connect/login_success.html";
定义您的应用需要的许可范围
String scope = "publish_stream,offline_access,read_stream,user_about_me";
为了让非死不可的授权认证显示页面方便在手机尺寸上显示,您还需要在request中增加额外的参数,该参数不是OAuth的一部分,但下面的代码会向您展示Spring Social组件如何支持传递该参数。
MultiValueMap<String, String> additionalParameters = new LinkedMultiValueMap<String, String>();
additionalParameters.add("display", "touch");
现在我们可以生成非死不可的认证页面url,并用在浏览器或者webView中。
OAuth2Parameters parameters = new OAuth2Parameters(redirectUri, scope, null, additionalParameters);
OAuth2Operations oauth = connectionFactory.getOAuthOperations();
String authorizeUrl = oauth.buildAuthorizeUrl(GrantType.IMPLICIT_GRANT, parameters);
那接下来的一步就是用户在webView或浏览器中登录并授权您的应用。浏览器将会重定向到刚刚指定的成功页面。如果认证通过,成功页面的URI片段中将会包含access_token 参数,获取访问令牌然后创建非死不可的连接吧。
AccessGrant accessGrant = new AccessGrant(accessToken);
Connection<FacebookApi> connection = connectionFactory.createConnection(accessGrant);
connectionRepository.addConnection(connection);
4.完整的例子
完整的例子可参考sample Android application.
$ git clone git://github.com/SpringSource/spring-android-samples.git